diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 9cdcd990e..158c0b4a2 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -51,6 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ESP32-S3: `PsramConfig::core_clock` is now an `Option` (#3974) - `RtcSlowClock::RtcFastClock8m` has been renamed to `RtcFastClock::RtcFastClockRcFast` (#3993) - `RtcSlowClock::RtcSlowClockRtc` has been renamed to `RtcSlowClock::RtcSlowClockRcSlow` (#3993) +- The `Raw: RawChannelAccess` of `rmt::Channel` has been erased; channel numbers are always dynamic now. (#3980) ### Fixed diff --git a/esp-hal/MIGRATING-1.0.0-rc.0.md b/esp-hal/MIGRATING-1.0.0-rc.0.md index b3801eb22..a9cc2de56 100644 --- a/esp-hal/MIGRATING-1.0.0-rc.0.md +++ b/esp-hal/MIGRATING-1.0.0-rc.0.md @@ -79,7 +79,7 @@ esp_hal::interrupt::bind_interrupt( ); ``` -## RMT changes +## RMT PulseCode changes `PulseCode` used to be an extension trait implemented on `u32`. It is now a newtype struct, wrapping `u32`. @@ -113,6 +113,42 @@ let _ = tx_channel.transmit(&tx_data).wait().unwrap(); let _ = rx_channel.transmit(&mut rx_data).wait().unwrap(); ``` +## RMT Channel Changes + +`rmt::Channel` used to have a `Raw: RawChannelAccess` generic parameter, +which could be either `ConstChannelAccess` or `DynChannelAccess`. +This generic has been erased, effectively always using `DynChannelAccess`. +The corresponding parameter of the transaction structs has been removed as well +(`SingleShotTxTransaction`, `ContinuousTxTransaction`, `RxTransaction`). + +Transmit and receive methods are now directly implemented by +`Channel` +and +`Channel` +respectively and the `RxChannel`, `TxChannel`, `RxChannelAsync` and `TxChannelAsync` +traits have been removed. +Several related types that were previously exported have been removed from the +API as well. + +```diff +-use esp_hal::rmt::{ConstChannelAccess, DynChannelAccess, RawChannelAccess}; +-let mut tx: Channel> = rmt.channel0.configure_tx(NoPin, TxChannelConfig::default()); +-let mut rx: Channel> = rmt.channel2.configure_rx(NoPin, RxChannelConfig::default()); ++let mut tx: Channel = rmt.channel0.configure_tx(NoPin, TxChannelConfig::default()); ++let mut rx: Channel = rmt.channel2.configure_rx(NoPin, RxChannelConfig::default()); + +-let mut tx: Channel> = tx.degrade(); +-let mut rx: Channel> = rx.degrade(); + +-// same for TxChannelAsync, RxChannelAsync +-use esp_hal::rmt::{TxChannel, RxChannel}; +- +-let tx_transaction: SingleShotTxTransaction<'_, DynChannelAccess, PulseCode> = tx.transmit(&data); +-let rx_transaction: RxTransaction<'_, DynChannelAccess, PulseCode> = rx.transmit(&data); ++let tx_transaction: SingleShotTxTransaction<'_, PulseCode> = tx.transmit(&data); ++let rx_transaction: RxTransaction<'_, PulseCode> = rx.transmit(&data); +``` + ## DMA changes DMA buffers now have a `Final` associated type parameter. For the publicly available buffer, this is `Self`, @@ -184,4 +220,4 @@ let peripherals = esp_hal::init( + .with_data5(peripherals.GPIO17) + .with_data6(peripherals.GPIO16) + .with_data7(peripherals.GPIO15); -``` \ No newline at end of file +``` diff --git a/esp-hal/src/rmt.rs b/esp-hal/src/rmt.rs index 885bca9e7..1598b6279 100644 --- a/esp-hal/src/rmt.rs +++ b/esp-hal/src/rmt.rs @@ -77,7 +77,7 @@ //! # {before_snippet} //! # use esp_hal::delay::Delay; //! # use esp_hal::gpio::Level; -//! # use esp_hal::rmt::{PulseCode, Rmt, TxChannel, TxChannelConfig, TxChannelCreator}; +//! # use esp_hal::rmt::{PulseCode, Rmt, TxChannelConfig, TxChannelCreator}; //! //! // Configure frequency based on chip type //! # {freq} @@ -104,7 +104,7 @@ //! ### RX operation //! ```rust, no_run //! # {before_snippet} -//! # use esp_hal::rmt::{PulseCode, Rmt, RxChannel, RxChannelConfig, RxChannelCreator}; +//! # use esp_hal::rmt::{PulseCode, Rmt, RxChannelConfig, RxChannelCreator}; //! # use esp_hal::delay::Delay; //! # use esp_hal::gpio::{Level, Output, OutputConfig}; //! @@ -490,10 +490,8 @@ impl From for u32 { /// extra space. However, it is useful to abstract memory sizes into their own /// type to make explicit whether they refer to a number of RAM blocks or a /// number of pulse codes and to centralize conversion between both. -// This currently needs to be `pub` because it is accessible via the *ChannelInternal traits. -#[doc(hidden)] #[derive(Copy, Clone)] -pub struct MemSize(u8); +struct MemSize(u8); impl MemSize { /// Create from the given number of RMT RAM blocks. @@ -528,7 +526,7 @@ pub struct Rx; /// For internal use by the driver. pub trait Direction: Copy + Clone + core::fmt::Debug + crate::private::Sealed { #[doc(hidden)] - fn is_tx() -> bool; + const IS_TX: bool; } impl crate::private::Sealed for Tx {} @@ -536,103 +534,29 @@ impl crate::private::Sealed for Tx {} impl crate::private::Sealed for Rx {} impl Direction for Tx { - #[inline] - fn is_tx() -> bool { - true - } + const IS_TX: bool = true; } impl Direction for Rx { - #[inline] - fn is_tx() -> bool { - false - } + const IS_TX: bool = false; } -/// An identifier for one channel of the RMT peripherial. -pub trait RawChannelAccess: Clone + Copy + core::fmt::Debug + crate::private::Sealed { - // Tx or Rx - #[doc(hidden)] - type Dir: Direction; - - #[doc(hidden)] - fn channel(&self) -> u8; -} - -/// A compile-time constant identifier for one channel of the RMT peripherial. -/// -/// This is a ZST. #[derive(Clone, Copy, Debug)] -pub struct ConstChannelAccess { +struct DynChannelAccess { + ch_idx: ChannelIndex, _direction: PhantomData, } -/// Type-erased equivalent of ConstChannelAccess. -#[derive(Clone, Copy, Debug)] -pub struct DynChannelAccess { - channel: u8, - _direction: PhantomData, -} - -impl crate::private::Sealed - for ConstChannelAccess -{ -} -impl crate::private::Sealed for DynChannelAccess {} - -impl ConstChannelAccess -where - Dir: Direction, - ConstChannelAccess: RawChannelAccess, -{ - const unsafe fn conjure() -> Self { - Self { - _direction: PhantomData, - } - } -} - impl DynChannelAccess { #[inline] - unsafe fn conjure(channel: u8) -> Self { + unsafe fn conjure(ch_idx: ChannelIndex) -> Self { Self { - channel, + ch_idx, _direction: PhantomData, } } } -impl RawChannelAccess for ConstChannelAccess { - type Dir = Tx; - - #[inline] - fn channel(&self) -> u8 { - CHANNEL - } -} - -impl RawChannelAccess for ConstChannelAccess { - type Dir = Rx; - - #[inline] - fn channel(&self) -> u8 { - CHANNEL - } -} - -impl RawChannelAccess for DynChannelAccess { - type Dir = Dir; - - #[inline] - fn channel(&self) -> u8 { - self.channel - } -} - -/// Alias for a type-erased channels configured for tx. -pub type AnyTxChannel = Channel>; -/// Alias for a type-erased channels configured for rx. -pub type AnyRxChannel = Channel>; /// Channel configuration for TX channels #[derive(Debug, Copy, Clone, procmacros::BuilderLite)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -752,6 +676,25 @@ macro_rules! declare_channels { macro_rules! declare_tx_channels { ($([$num:literal, $idx:literal]),+ $(,)?) => { paste::paste! { + // Enum of valid channel indices: For the given chip, tx/rx channels for all of these indices + // exist. (Note that channel index == channel number for esp32 and esp32s2, but not for other + // chips.) + // + // This type is useful to inform the compiler of possible values of an u8 (i.e. we use this as + // homemade refinement type) which allows it to elide bounds checks in register/field + // accessors of the PAC even when using DynChannelAccess. + // + // Cf. https://github.com/rust-lang/rust/issues/109958 regarding rustc's capabilities here. + #[doc(hidden)] + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + #[repr(u8)] + #[allow(unused)] + pub enum ChannelIndex { + $( + [< Ch $idx>] = $idx, + )+ + } + #[allow(clippy::no_effect)] const CHANNEL_INDEX_COUNT: u8 = const { 0 $( + {$idx; 1} )+ }; @@ -766,8 +709,18 @@ macro_rules! declare_tx_channels { where Dm: crate::DriverMode, { - type Raw = ConstChannelAccess; - const RAW: Self::Raw = unsafe { ConstChannelAccess::conjure() }; + fn configure_tx( + self, + pin: impl PeripheralOutput<'d>, + config: TxChannelConfig, + ) -> Result, Error> + where + Self: Sized, + { + let raw = unsafe { DynChannelAccess::conjure(ChannelIndex::[]) }; + configure_tx_channel(raw, pin.into(), config)?; + Ok(Channel::new(raw)) + } } )+ } @@ -788,14 +741,51 @@ macro_rules! declare_rx_channels { where Dm: crate::DriverMode, { - type Raw = ConstChannelAccess<$crate::rmt::Rx, $num>; - const RAW: Self::Raw = unsafe { ConstChannelAccess::conjure() }; + fn configure_rx( + self, + pin: impl PeripheralInput<'d>, + config: RxChannelConfig, + ) -> Result, Error> + where + Self: Sized, + { + let raw = unsafe { DynChannelAccess::conjure(ChannelIndex::[]) }; + configure_rx_channel(raw, pin.into(), config)?; + Ok(Channel::new(raw)) + } } )+ } }; } +struct ChannelIndexIter(u8); + +impl Iterator for ChannelIndexIter { + type Item = ChannelIndex; + + fn next(&mut self) -> Option { + let ch_idx = self.0; + if ch_idx < CHANNEL_INDEX_COUNT { + self.0 = ch_idx + 1; + Some(unsafe { ChannelIndex::from_u8_unchecked(ch_idx) }) + } else { + None + } + } +} + +impl ChannelIndex { + fn iter_all() -> ChannelIndexIter { + ChannelIndexIter(0) + } + + unsafe fn from_u8_unchecked(ch_idx: u8) -> Self { + debug_assert!(ch_idx < CHANNEL_INDEX_COUNT); + unsafe { core::mem::transmute(ch_idx) } + } +} + cfg_if::cfg_if! { if #[cfg(esp32)] { declare_channels!(0, 1, 2, 3, 4, 5, 6, 7); @@ -937,8 +927,8 @@ fn reserve_channel(channel: u8, state: RmtState, memsize: MemSize) -> Result<(), } fn configure_rx_channel<'d>( - raw: impl RxChannelInternal, - pin: impl PeripheralInput<'d>, + raw: DynChannelAccess, + pin: gpio::interconnect::InputSignal<'d>, config: RxChannelConfig, ) -> Result<(), Error> { let threshold = if cfg!(any(esp32, esp32s2)) { @@ -954,8 +944,6 @@ fn configure_rx_channel<'d>( let memsize = MemSize::from_blocks(config.memsize); reserve_channel(raw.channel(), RmtState::Rx, memsize)?; - let pin = pin.into(); - pin.apply_input_config(&InputConfig::default()); pin.set_input_enable(true); @@ -976,15 +964,13 @@ fn configure_rx_channel<'d>( } fn configure_tx_channel<'d>( - raw: impl TxChannelInternal, - pin: impl PeripheralOutput<'d>, + raw: DynChannelAccess, + pin: gpio::interconnect::OutputSignal<'d>, config: TxChannelConfig, ) -> Result<(), Error> { let memsize = MemSize::from_blocks(config.memsize); reserve_channel(raw.channel(), RmtState::Tx, memsize)?; - let pin = pin.into(); - pin.apply_output_config(&OutputConfig::default()); pin.set_output_enable(true); @@ -1012,7 +998,7 @@ fn configure_tx_channel<'d>( mod state { use portable_atomic::{AtomicU8, Ordering}; - use super::{Direction, NUM_CHANNELS, RawChannelAccess}; + use super::{Direction, DynChannelAccess, NUM_CHANNELS}; static STATE: [AtomicU8; NUM_CHANNELS] = [const { AtomicU8::new(RmtState::Unconfigured as u8) }; NUM_CHANNELS]; @@ -1057,9 +1043,13 @@ mod state { } /// Load channel state from the global `STATE` by channel index. + /// + /// # Safety: + /// + /// - the `channel` number must be in 0..NUM_CHANNELS #[allow(unused)] #[inline] - pub(super) fn load_by_idx(channel: u8, ordering: Ordering) -> Self { + pub(super) unsafe fn load_by_channel_number(channel: u8, ordering: Ordering) -> Self { unsafe { Self::from_u8_unchecked(STATE[channel as usize].load(ordering)) } } @@ -1071,21 +1061,23 @@ mod state { } } - /// Store channel state to the global `STATE` given a `RawChannelAccess`. + /// Store channel state to the global `STATE` given a `DynChannelAccess`. #[allow(unused)] #[inline] - pub(super) fn store(self, raw: impl RawChannelAccess, ordering: Ordering) { + pub(super) fn store(self, raw: DynChannelAccess, ordering: Ordering) { STATE[raw.channel() as usize].store(self as u8, ordering); } - /// Load channel state from the global `STATE` given a `RawChannelAccess`. + /// Load channel state from the global `STATE` given a `DynChannelAccess`. #[allow(unused)] #[inline] - pub(super) fn load(raw: impl RawChannelAccess, ordering: Ordering) -> Self { - Self::load_by_idx(raw.channel(), ordering) + pub(super) fn load(raw: DynChannelAccess, ordering: Ordering) -> Self { + // SAFETY: The only implementations of RawChannelAccess are in this module and can only + // be (safely) constructed using valid channel numbers. + unsafe { Self::load_by_channel_number(raw.channel(), ordering) } } - /// Perform a compare_exchange for the channel state for a `RawChannelAccess`. + /// Perform a compare_exchange on the global `STATE` by channel index. #[inline] pub(super) fn compare_exchange( ch_num: u8, @@ -1107,22 +1099,22 @@ use state::RmtState; /// RMT Channel #[derive(Debug)] #[non_exhaustive] -pub struct Channel +pub struct Channel where Dm: crate::DriverMode, - Raw: ChannelInternal, + Dir: Direction, { - raw: Raw, + raw: DynChannelAccess, _mode: PhantomData, _guard: GenericPeripheralGuard<{ system::Peripheral::Rmt as u8 }>, } -impl Channel +impl Channel where Dm: crate::DriverMode, - Raw: ChannelInternal, + Dir: Direction, { - fn new(raw: Raw) -> Self { + fn new(raw: DynChannelAccess) -> Self { Self { raw, _mode: core::marker::PhantomData, @@ -1131,35 +1123,10 @@ where } } -// Note that this is intentionally implemented even if Raw is DynChannelAccess -// already for convenience! -impl Channel +impl Drop for Channel where Dm: crate::DriverMode, Dir: Direction, - Raw: RawChannelAccess, -{ - /// Consume the channel and return a type-erased version - pub fn degrade(self) -> Channel> { - use core::mem::ManuallyDrop; - // Disable Drop handler on self - let old = ManuallyDrop::new(self); - Channel { - raw: DynChannelAccess { - channel: old.raw.channel(), - _direction: PhantomData, - }, - _mode: PhantomData, - // FIXME: Don't clone, but move old._guard - _guard: old._guard.clone(), - } - } -} - -impl Drop for Channel -where - Dm: crate::DriverMode, - Raw: ChannelInternal, { fn drop(&mut self) { let memsize = self.raw.memsize(); @@ -1184,24 +1151,14 @@ pub trait TxChannelCreator<'d, Dm> where Dm: crate::DriverMode, { - /// Type of the raw channel access token - type Raw: TxChannelInternal; - - #[doc(hidden)] - const RAW: Self::Raw; - /// Configure the TX channel fn configure_tx( self, pin: impl PeripheralOutput<'d>, config: TxChannelConfig, - ) -> Result, Error> + ) -> Result, Error> where - Self: Sized, - { - configure_tx_channel(Self::RAW, pin, config)?; - Ok(Channel::new(Self::RAW)) - } + Self: Sized; } /// Creates a RX channel @@ -1209,24 +1166,14 @@ pub trait RxChannelCreator<'d, Dm> where Dm: crate::DriverMode, { - /// Type of the raw channel access token - type Raw: RxChannelInternal; - - #[doc(hidden)] - const RAW: Self::Raw; - /// Configure the RX channel fn configure_rx( self, pin: impl PeripheralInput<'d>, config: RxChannelConfig, - ) -> Result, Error> + ) -> Result, Error> where - Self: Sized, - { - configure_rx_channel(Self::RAW, pin, config)?; - Ok(Channel::new(Self::RAW)) - } + Self: Sized; } /// An in-progress transaction for a single shot TX transaction. @@ -1234,12 +1181,11 @@ where /// If the data size exceeds the size of the internal buffer, `.poll()` or /// `.wait()` needs to be called before the entire buffer has been sent to avoid /// underruns. -pub struct SingleShotTxTransaction<'a, Raw, T> +pub struct SingleShotTxTransaction<'a, T> where - Raw: TxChannelInternal, T: Into + Copy, { - channel: Channel, + channel: Channel, // The position in channel RAM to continue writing at; must be either // 0 or half the available RAM size if there's further data. @@ -1250,9 +1196,8 @@ where remaining_data: &'a [T], } -impl SingleShotTxTransaction<'_, Raw, T> +impl SingleShotTxTransaction<'_, T> where - Raw: TxChannelInternal, T: Into + Copy, { #[cfg_attr(place_rmt_driver_in_ram, ram)] @@ -1308,7 +1253,7 @@ where /// Wait for the transaction to complete #[cfg_attr(place_rmt_driver_in_ram, inline(always))] - pub fn wait(mut self) -> Result, (Error, Channel)> { + pub fn wait(mut self) -> Result, (Error, Channel)> { // Not sure that all the error cases below can happen. However, it's best to // handle them to be sure that we don't lock up here in case they can happen. loop { @@ -1330,14 +1275,14 @@ where } /// An in-progress continuous TX transaction -pub struct ContinuousTxTransaction { - channel: Channel, +pub struct ContinuousTxTransaction { + channel: Channel, } -impl ContinuousTxTransaction { +impl ContinuousTxTransaction { /// Stop transaction when the current iteration ends. #[cfg_attr(place_rmt_driver_in_ram, ram)] - pub fn stop_next(self) -> Result, (Error, Channel)> { + pub fn stop_next(self) -> Result, (Error, Channel)> { let raw = self.channel.raw; raw.set_tx_continuous(false); @@ -1354,7 +1299,7 @@ impl ContinuousTxTransaction { /// Stop transaction as soon as possible. #[cfg_attr(place_rmt_driver_in_ram, ram)] - pub fn stop(self) -> Result, (Error, Channel)> { + pub fn stop(self) -> Result, (Error, Channel)> { let raw = self.channel.raw; raw.set_tx_continuous(false); @@ -1415,49 +1360,13 @@ impl ChannelCreator { } /// Channel in TX mode -pub trait TxChannel: Sized { - /// Channel identifier of the implementing channel. - type Raw: TxChannelInternal; - +impl Channel { /// Start transmitting the given pulse code sequence. /// This returns a [`SingleShotTxTransaction`] which can be used to wait for /// the transaction to complete and get back the channel for further /// use. - fn transmit(self, data: &[T]) -> Result, Error> - where - T: Into + Copy; - - /// Start transmitting the given pulse code continuously. - /// This returns a [`ContinuousTxTransaction`] which can be used to stop the - /// ongoing transmission and get back the channel for further use. - /// The length of sequence cannot exceed the size of the allocated RMT RAM. - fn transmit_continuously( - self, - data: &[T], - ) -> Result, Error> - where - T: Into + Copy; - - /// Like [`Self::transmit_continuously`] but also sets a loop count. - /// [`ContinuousTxTransaction`] can be used to check if the loop count is - /// reached. - fn transmit_continuously_with_loopcount( - self, - loopcount: u16, - data: &[T], - ) -> Result, Error> - where - T: Into + Copy; -} - -impl TxChannel for Channel -where - Raw: TxChannelInternal, -{ - type Raw = Raw; - #[cfg_attr(place_rmt_driver_in_ram, ram)] - fn transmit(self, data: &[T]) -> Result, Error> + pub fn transmit(self, data: &[T]) -> Result, Error> where T: Into + Copy, { @@ -1470,20 +1379,27 @@ where }) } + /// Start transmitting the given pulse code continuously. + /// This returns a [`ContinuousTxTransaction`] which can be used to stop the + /// ongoing transmission and get back the channel for further use. + /// The length of sequence cannot exceed the size of the allocated RMT RAM. #[inline] - fn transmit_continuously(self, data: &[T]) -> Result, Error> + pub fn transmit_continuously(self, data: &[T]) -> Result where T: Into + Copy, { self.transmit_continuously_with_loopcount(0, data) } + /// Like [`Self::transmit_continuously`] but also sets a loop count. + /// [`ContinuousTxTransaction`] can be used to check if the loop count is + /// reached. #[cfg_attr(place_rmt_driver_in_ram, ram)] - fn transmit_continuously_with_loopcount( + pub fn transmit_continuously_with_loopcount( self, loopcount: u16, data: &[T], - ) -> Result, Error> + ) -> Result where T: Into + Copy, { @@ -1497,17 +1413,16 @@ where } /// RX transaction instance -pub struct RxTransaction<'a, Raw: RxChannelInternal, T> +pub struct RxTransaction<'a, T> where T: From, { - channel: Channel, + channel: Channel, data: &'a mut [T], } -impl RxTransaction<'_, Raw, T> +impl RxTransaction<'_, T> where - Raw: RxChannelInternal, T: From, { #[cfg_attr(place_rmt_driver_in_ram, ram)] @@ -1546,7 +1461,7 @@ where /// Wait for the transaction to complete #[cfg_attr(place_rmt_driver_in_ram, inline(always))] - pub fn wait(mut self) -> Result, (Error, Channel)> { + pub fn wait(mut self) -> Result, (Error, Channel)> { let raw = self.channel.raw; let result = loop { @@ -1564,27 +1479,13 @@ where } /// Channel is RX mode -pub trait RxChannel: Sized { - /// Channel identifier of the implementing channel. - type Raw: RxChannelInternal; - +impl Channel { /// Start receiving pulse codes into the given buffer. /// This returns a [RxTransaction] which can be used to wait for receive to /// complete and get back the channel for further use. /// The length of the received data cannot exceed the allocated RMT RAM. - fn receive(self, data: &mut [T]) -> Result, Error> - where - T: From; -} - -impl RxChannel for Channel -where - Raw: RxChannelInternal, -{ - type Raw = Raw; - #[cfg_attr(place_rmt_driver_in_ram, ram)] - fn receive(self, data: &mut [T]) -> Result, Error> + pub fn receive(self, data: &mut [T]) -> Result, Error> where Self: Sized, T: From, @@ -1604,17 +1505,11 @@ where static WAKER: [AtomicWaker; NUM_CHANNELS] = [const { AtomicWaker::new() }; NUM_CHANNELS]; #[must_use = "futures do nothing unless you `.await` or poll them"] -pub(crate) struct RmtTxFuture -where - Raw: TxChannelInternal, -{ - raw: Raw, +struct RmtTxFuture { + raw: DynChannelAccess, } -impl core::future::Future for RmtTxFuture -where - Raw: TxChannelInternal, -{ +impl core::future::Future for RmtTxFuture { type Output = Result<(), Error>; #[cfg_attr(place_rmt_driver_in_ram, ram)] @@ -1630,22 +1525,12 @@ where } /// TX channel in async mode -pub trait TxChannelAsync { +impl Channel { /// Start transmitting the given pulse code sequence. /// The length of sequence cannot exceed the size of the allocated RMT /// RAM. - async fn transmit(&mut self, data: &[T]) -> Result<(), Error> - where - Self: Sized, - T: Into + Copy; -} - -impl TxChannelAsync for Channel -where - Raw: TxChannelInternal, -{ #[cfg_attr(place_rmt_driver_in_ram, ram)] - async fn transmit(&mut self, data: &[T]) -> Result<(), Error> + pub async fn transmit(&mut self, data: &[T]) -> Result<(), Error> where Self: Sized, T: Into + Copy, @@ -1665,17 +1550,11 @@ where } #[must_use = "futures do nothing unless you `.await` or poll them"] -pub(crate) struct RmtRxFuture -where - Raw: RxChannelInternal, -{ - raw: Raw, +struct RmtRxFuture { + raw: DynChannelAccess, } -impl core::future::Future for RmtRxFuture -where - Raw: RxChannelInternal, -{ +impl core::future::Future for RmtRxFuture { type Output = Result<(), Error>; #[cfg_attr(place_rmt_driver_in_ram, ram)] @@ -1691,22 +1570,12 @@ where } /// RX channel in async mode -pub trait RxChannelAsync { +impl Channel { /// Start receiving a pulse code sequence. /// The length of sequence cannot exceed the size of the allocated RMT /// RAM. - async fn receive(&mut self, data: &mut [T]) -> Result<(), Error> - where - Self: Sized, - T: From; -} - -impl RxChannelAsync for Channel -where - Raw: RxChannelInternal, -{ #[cfg_attr(place_rmt_driver_in_ram, ram)] - async fn receive(&mut self, data: &mut [T]) -> Result<(), Error> + pub async fn receive(&mut self, data: &mut [T]) -> Result<(), Error> where Self: Sized, T: From, @@ -1739,57 +1608,32 @@ where } } -#[cfg(not(any(esp32, esp32s2)))] #[handler] fn async_interrupt_handler() { - let Some((channel, is_tx)) = chip_specific::pending_interrupt_for_channel() else { - return; - }; - if is_tx { - unsafe { DynChannelAccess::::conjure(channel) } - .unlisten_tx_interrupt(Event::End | Event::Error); - } else { - unsafe { DynChannelAccess::::conjure(channel) } - .unlisten_rx_interrupt(Event::End | Event::Error); + fn on_tx(raw: DynChannelAccess) { + raw.unlisten_tx_interrupt(Event::End | Event::Error); + + WAKER[raw.channel() as usize].wake(); } - WAKER[channel as usize].wake(); -} + fn on_rx(raw: DynChannelAccess) { + raw.unlisten_rx_interrupt(Event::End | Event::Error); -#[cfg(any(esp32, esp32s2))] -#[handler] -fn async_interrupt_handler() { - let Some(channel) = chip_specific::pending_interrupt_for_channel() else { - return; - }; + WAKER[raw.channel() as usize].wake(); + } - unsafe { DynChannelAccess::::conjure(channel) } - .unlisten_tx_interrupt(Event::End | Event::Error); - unsafe { DynChannelAccess::::conjure(channel) } - .unlisten_rx_interrupt(Event::End | Event::Error); - - WAKER[channel as usize].wake(); + chip_specific::handle_channel_interrupts(on_tx, on_rx); } #[derive(Debug, EnumSetType)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[doc(hidden)] -pub enum Event { +enum Event { Error, Threshold, End, } -#[doc(hidden)] -pub trait ChannelInternal: RawChannelAccess { - fn update(&self); - - fn set_divider(&self, divider: u8); - - fn memsize(&self) -> MemSize; - - fn set_memsize(&self, value: MemSize); - +impl DynChannelAccess { #[inline] fn channel_ram_start(&self) -> *mut PulseCode { unsafe { @@ -1799,36 +1643,11 @@ pub trait ChannelInternal: RawChannelAccess { } } -#[doc(hidden)] -pub trait TxChannelInternal: ChannelInternal { +impl DynChannelAccess { fn output_signal(&self) -> gpio::OutputSignal { - OUTPUT_SIGNALS[self.channel() as usize] + OUTPUT_SIGNALS[self.ch_idx as usize] } - fn set_generate_repeat_interrupt(&self, repeats: u16); - - fn clear_tx_interrupts(&self); - - fn set_tx_continuous(&self, continuous: bool); - - fn set_tx_wrap_mode(&self, wrap: bool); - - fn set_tx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level); - - fn set_tx_idle_output(&self, enable: bool, level: Level); - - fn start_tx(&self); - - // Return the first flag that is set of, in order of decreasing priority, - // Event::Error, Event::End, Event::Threshold - fn get_tx_status(&self) -> Option; - - fn reset_tx_threshold_set(&self); - - fn set_tx_threshold(&self, threshold: u8); - - fn is_tx_loopcount_interrupt_set(&self) -> bool; - #[inline] fn start_send(&self, data: &[T], continuous: bool, repeat: u16) -> Result where @@ -1863,10 +1682,6 @@ pub trait TxChannelInternal: ChannelInternal { Ok(data.len().min(memsize)) } - fn stop_tx(&self); - - fn set_tx_interrupt(&self, event: EnumSet, enable: bool); - #[inline] fn listen_tx_interrupt(&self, event: impl Into>) { self.set_tx_interrupt(event.into(), true); @@ -1878,24 +1693,11 @@ pub trait TxChannelInternal: ChannelInternal { } } -#[doc(hidden)] -pub trait RxChannelInternal: ChannelInternal { +impl DynChannelAccess { fn input_signal(&self) -> gpio::InputSignal { - INPUT_SIGNALS[self.channel() as usize] + INPUT_SIGNALS[self.ch_idx as usize] } - fn clear_rx_interrupts(&self); - - fn set_rx_wrap_mode(&self, wrap: bool); - - fn set_rx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level); - - fn start_rx(&self); - - // Return the first flag that is set of, in order of decreasing priority, - // Event::Error, Event::End, Event::Threshold - fn get_rx_status(&self) -> Option; - fn start_receive(&self) { self.clear_rx_interrupts(); self.set_rx_wrap_mode(false); @@ -1903,14 +1705,6 @@ pub trait RxChannelInternal: ChannelInternal { self.update(); } - fn stop_rx(&self); - - fn set_rx_filter_threshold(&self, value: u8); - - fn set_rx_idle_threshold(&self, value: u16); - - fn set_rx_interrupt(&self, event: EnumSet, enable: bool); - #[inline] fn listen_rx_interrupt(&self, event: impl Into>) { self.set_rx_interrupt(event.into(), true); @@ -1927,20 +1721,18 @@ mod chip_specific { use enumset::EnumSet; use super::{ - ChannelInternal, + CHANNEL_INDEX_COUNT, + ChannelIndex, Direction, + DynChannelAccess, Error, Event, Level, MemSize, - NUM_CHANNELS, - RawChannelAccess, Rx, - RxChannelInternal, Tx, - TxChannelInternal, }; - use crate::{gpio, peripherals::RMT, time::Rate}; + use crate::{peripherals::RMT, time::Rate}; pub(super) fn configure_clock(frequency: Rate) -> Result<(), Error> { let src_clock = crate::soc::constants::RMT_CLOCK_SRC_FREQ; @@ -1993,46 +1785,42 @@ mod chip_specific { #[allow(unused)] #[inline] - pub(super) fn pending_interrupt_for_channel() -> Option<(u8, bool)> { + pub(super) fn handle_channel_interrupts( + on_tx: fn(DynChannelAccess), + on_rx: fn(DynChannelAccess), + ) { let st = RMT::regs().int_st().read(); - for ch_idx in 0..NUM_CHANNELS as u8 / 2 { - if st.ch_tx_end(ch_idx).bit() || st.ch_tx_err(ch_idx).bit() { - // The first half of all channels support tx... - let ch_num = ch_idx; - return Some((ch_num, true)); + for ch_idx in ChannelIndex::iter_all() { + if st.ch_tx_end(ch_idx as u8).bit() || st.ch_tx_err(ch_idx as u8).bit() { + let raw = unsafe { DynChannelAccess::::conjure(ch_idx) }; + on_tx(raw); + return; } - if st.ch_rx_end(ch_idx).bit() || st.ch_rx_err(ch_idx).bit() { - // ...whereas the second half of channels support rx. - let ch_num = NUM_CHANNELS as u8 / 2 + ch_idx; - return Some((ch_num, false)); + if st.ch_rx_end(ch_idx as u8).bit() || st.ch_rx_err(ch_idx as u8).bit() { + let raw = unsafe { DynChannelAccess::::conjure(ch_idx) }; + on_rx(raw); + return; } } - - None - } - - // The index of this channel among all Tx/Rx channels (which may be different - // from the channel number). - #[inline] - fn ch_idx(raw: &impl RawChannelAccess) -> u8 { - if Dir::is_tx() { - raw.channel() - } else { - raw.channel() - NUM_CHANNELS as u8 / 2 - } } - impl ChannelInternal for A - where - A: RawChannelAccess, - { + impl DynChannelAccess { #[inline] - fn update(&self) { - let rmt = crate::peripherals::RMT::regs(); - let ch_idx = ch_idx(self) as usize; + pub fn channel(&self) -> u8 { + if Dir::IS_TX { + self.ch_idx as u8 + } else { + self.ch_idx as u8 + CHANNEL_INDEX_COUNT + } + } - if A::Dir::is_tx() { + #[inline] + pub fn update(&self) { + let rmt = crate::peripherals::RMT::regs(); + let ch_idx = self.ch_idx as usize; + + if Dir::IS_TX { rmt.ch_tx_conf0(ch_idx) .modify(|_, w| w.conf_update().set_bit()); } else { @@ -2041,11 +1829,11 @@ mod chip_specific { } } - fn set_divider(&self, divider: u8) { + pub fn set_divider(&self, divider: u8) { let rmt = crate::peripherals::RMT::regs(); - let ch_idx = ch_idx(self) as usize; + let ch_idx = self.ch_idx as usize; - if A::Dir::is_tx() { + if Dir::IS_TX { rmt.ch_tx_conf0(ch_idx) .modify(|_, w| unsafe { w.div_cnt().bits(divider) }); } else { @@ -2055,11 +1843,11 @@ mod chip_specific { } #[inline] - fn memsize(&self) -> MemSize { + pub fn memsize(&self) -> MemSize { let rmt = RMT::regs(); - let ch_idx = ch_idx(self) as usize; + let ch_idx = self.ch_idx as usize; - let blocks = if A::Dir::is_tx() { + let blocks = if Dir::IS_TX { rmt.ch_tx_conf0(ch_idx).read().mem_size().bits() } else { rmt.ch_rx_conf0(ch_idx).read().mem_size().bits() @@ -2068,12 +1856,12 @@ mod chip_specific { MemSize::from_blocks(blocks) } - fn set_memsize(&self, value: MemSize) { + pub fn set_memsize(&self, value: MemSize) { let blocks = value.blocks(); let rmt = RMT::regs(); - let ch_idx = ch_idx(self) as usize; + let ch_idx = self.ch_idx as usize; - if A::Dir::is_tx() { + if Dir::IS_TX { rmt.ch_tx_conf0(ch_idx) .modify(|_, w| unsafe { w.mem_size().bits(blocks) }); } else { @@ -2083,12 +1871,9 @@ mod chip_specific { } } - impl TxChannelInternal for A - where - A: RawChannelAccess, - { + impl DynChannelAccess { #[inline] - fn set_generate_repeat_interrupt(&self, repeats: u16) { + pub fn set_generate_repeat_interrupt(&self, repeats: u16) { let rmt = crate::peripherals::RMT::regs(); if repeats > 1 { rmt.ch_tx_lim(self.channel().into()).modify(|_, w| unsafe { @@ -2109,7 +1894,7 @@ mod chip_specific { } #[inline] - fn clear_tx_interrupts(&self) { + pub fn clear_tx_interrupts(&self) { let rmt = crate::peripherals::RMT::regs(); rmt.int_clr().write(|w| { @@ -2121,7 +1906,7 @@ mod chip_specific { } #[inline] - fn set_tx_continuous(&self, continuous: bool) { + pub fn set_tx_continuous(&self, continuous: bool) { let rmt = crate::peripherals::RMT::regs(); rmt.ch_tx_conf0(self.channel().into()) @@ -2129,14 +1914,14 @@ mod chip_specific { } #[inline] - fn set_tx_wrap_mode(&self, wrap: bool) { + pub fn set_tx_wrap_mode(&self, wrap: bool) { let rmt = crate::peripherals::RMT::regs(); rmt.ch_tx_conf0(self.channel().into()) .modify(|_, w| w.mem_tx_wrap_en().bit(wrap)); } - fn set_tx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level) { + pub fn set_tx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level) { let rmt = crate::peripherals::RMT::regs(); rmt.chcarrier_duty(self.channel().into()) @@ -2149,14 +1934,14 @@ mod chip_specific { }); } - fn set_tx_idle_output(&self, enable: bool, level: Level) { + pub fn set_tx_idle_output(&self, enable: bool, level: Level) { let rmt = crate::peripherals::RMT::regs(); rmt.ch_tx_conf0(self.channel().into()) .modify(|_, w| w.idle_out_en().bit(enable).idle_out_lv().bit(level.into())); } #[inline] - fn start_tx(&self) { + pub fn start_tx(&self) { let rmt = crate::peripherals::RMT::regs(); rmt.ch_tx_conf0(self.channel().into()).modify(|_, w| { @@ -2167,8 +1952,10 @@ mod chip_specific { self.update(); } + // Return the first flag that is set of, in order of decreasing priority, + // Event::Error, Event::End, Event::Threshold #[inline] - fn get_tx_status(&self) -> Option { + pub fn get_tx_status(&self) -> Option { let rmt = crate::peripherals::RMT::regs(); let reg = rmt.int_raw().read(); let ch = self.channel(); @@ -2185,27 +1972,28 @@ mod chip_specific { } #[inline] - fn reset_tx_threshold_set(&self) { + pub fn reset_tx_threshold_set(&self) { let rmt = crate::peripherals::RMT::regs(); rmt.int_clr() .write(|w| w.ch_tx_thr_event(self.channel()).set_bit()); } #[inline] - fn set_tx_threshold(&self, threshold: u8) { + pub fn set_tx_threshold(&self, threshold: u8) { let rmt = crate::peripherals::RMT::regs(); rmt.ch_tx_lim(self.channel().into()) .modify(|_, w| unsafe { w.tx_lim().bits(threshold as u16) }); } #[inline] - fn is_tx_loopcount_interrupt_set(&self) -> bool { + pub fn is_tx_loopcount_interrupt_set(&self) -> bool { let rmt = crate::peripherals::RMT::regs(); rmt.int_raw().read().ch_tx_loop(self.channel()).bit() } + #[allow(unused)] #[inline] - fn stop_tx(&self) { + pub fn stop_tx(&self) { let rmt = crate::peripherals::RMT::regs(); rmt.ch_tx_conf0(self.channel().into()) .modify(|_, w| w.tx_stop().set_bit()); @@ -2213,7 +2001,7 @@ mod chip_specific { } #[inline] - fn set_tx_interrupt(&self, events: EnumSet, enable: bool) { + pub fn set_tx_interrupt(&self, events: EnumSet, enable: bool) { let rmt = crate::peripherals::RMT::regs(); rmt.int_ena().modify(|_, w| { if events.contains(Event::Error) { @@ -2230,19 +2018,11 @@ mod chip_specific { } } - impl RxChannelInternal for A - where - A: RawChannelAccess, - { - fn input_signal(&self) -> gpio::InputSignal { - let ch_idx = ch_idx(self) as usize; - super::INPUT_SIGNALS[ch_idx] - } - + impl DynChannelAccess { #[inline] - fn clear_rx_interrupts(&self) { + pub fn clear_rx_interrupts(&self) { let rmt = crate::peripherals::RMT::regs(); - let ch_idx = ch_idx(self); + let ch_idx = self.ch_idx as u8; rmt.int_clr().write(|w| { w.ch_rx_end(ch_idx).set_bit(); @@ -2252,16 +2032,16 @@ mod chip_specific { } #[inline] - fn set_rx_wrap_mode(&self, wrap: bool) { + pub fn set_rx_wrap_mode(&self, wrap: bool) { let rmt = crate::peripherals::RMT::regs(); - let ch_idx = ch_idx(self) as usize; + let ch_idx = self.ch_idx as usize; rmt.ch_rx_conf1(ch_idx) .modify(|_, w| w.mem_rx_wrap_en().bit(wrap)); } - fn set_rx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level) { - let ch_idx = ch_idx(self) as usize; + pub fn set_rx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level) { + let ch_idx = self.ch_idx as usize; let rmt = crate::peripherals::RMT::regs(); rmt.ch_rx_carrier_rm(ch_idx).write(|w| unsafe { @@ -2278,9 +2058,9 @@ mod chip_specific { } #[inline] - fn start_rx(&self) { + pub fn start_rx(&self) { let rmt = crate::peripherals::RMT::regs(); - let ch_idx = ch_idx(self); + let ch_idx = self.ch_idx as u8; for i in 1..self.memsize().blocks() { rmt.ch_rx_conf1((ch_idx + i).into()) @@ -2294,11 +2074,13 @@ mod chip_specific { }); } + // Return the first flag that is set of, in order of decreasing priority, + // Event::Error, Event::End, Event::Threshold #[inline] - fn get_rx_status(&self) -> Option { + pub fn get_rx_status(&self) -> Option { let rmt = crate::peripherals::RMT::regs(); let reg = rmt.int_raw().read(); - let ch_idx = ch_idx(self); + let ch_idx = self.ch_idx as u8; if reg.ch_rx_end(ch_idx).bit() { Some(Event::End) @@ -2312,15 +2094,15 @@ mod chip_specific { } #[inline] - fn stop_rx(&self) { + pub fn stop_rx(&self) { let rmt = crate::peripherals::RMT::regs(); - let ch_idx = ch_idx(self) as usize; + let ch_idx = self.ch_idx as usize; rmt.ch_rx_conf1(ch_idx).modify(|_, w| w.rx_en().clear_bit()); } - fn set_rx_filter_threshold(&self, value: u8) { + pub fn set_rx_filter_threshold(&self, value: u8) { let rmt = crate::peripherals::RMT::regs(); - let ch_idx = ch_idx(self) as usize; + let ch_idx = self.ch_idx as usize; rmt.ch_rx_conf1(ch_idx).modify(|_, w| unsafe { w.rx_filter_en().bit(value > 0); @@ -2328,18 +2110,18 @@ mod chip_specific { }); } - fn set_rx_idle_threshold(&self, value: u16) { + pub fn set_rx_idle_threshold(&self, value: u16) { let rmt = crate::peripherals::RMT::regs(); - let ch_idx = ch_idx(self) as usize; + let ch_idx = self.ch_idx as usize; rmt.ch_rx_conf0(ch_idx) .modify(|_, w| unsafe { w.idle_thres().bits(value) }); } #[inline] - fn set_rx_interrupt(&self, events: EnumSet, enable: bool) { + pub fn set_rx_interrupt(&self, events: EnumSet, enable: bool) { let rmt = crate::peripherals::RMT::regs(); - let ch_idx = ch_idx(self); + let ch_idx = self.ch_idx as u8; rmt.int_ena().modify(|_, w| { if events.contains(Event::Error) { @@ -2360,20 +2142,20 @@ mod chip_specific { #[cfg(any(esp32, esp32s2))] mod chip_specific { use enumset::EnumSet; + use portable_atomic::Ordering; use super::{ - ChannelInternal, + ChannelIndex, Direction, + DynChannelAccess, Error, Event, Level, MemSize, NUM_CHANNELS, - RawChannelAccess, + RmtState, Rx, - RxChannelInternal, Tx, - TxChannelInternal, }; use crate::{peripherals::RMT, time::Rate}; @@ -2399,75 +2181,97 @@ mod chip_specific { #[allow(unused)] #[inline] - pub(super) fn pending_interrupt_for_channel() -> Option { - let rmt = RMT::regs(); - let st = rmt.int_st().read(); + pub(super) fn handle_channel_interrupts( + on_tx: fn(DynChannelAccess), + on_rx: fn(DynChannelAccess), + ) { + let st = RMT::regs().int_st().read(); - (0..NUM_CHANNELS as u8).find(|&ch_num| { - st.ch_rx_end(ch_num).bit() || st.ch_tx_end(ch_num).bit() || st.ch_err(ch_num).bit() - }) + for ch_idx in ChannelIndex::iter_all() { + if st.ch_rx_end(ch_idx as u8).bit() { + let raw = unsafe { DynChannelAccess::::conjure(ch_idx) }; + on_rx(raw); + return; + } + if st.ch_tx_end(ch_idx as u8).bit() { + let raw = unsafe { DynChannelAccess::::conjure(ch_idx) }; + on_tx(raw); + return; + } + if st.ch_err(ch_idx as u8).bit() { + // SAFETY: channel number == ch_idx for these chips, so the argument is correct and + // in range. + let state = + unsafe { RmtState::load_by_channel_number(ch_idx as u8, Ordering::Relaxed) }; + if let Some(is_tx) = state.is_tx() { + if is_tx { + let raw = unsafe { DynChannelAccess::::conjure(ch_idx) }; + on_tx(raw); + } else { + let raw = unsafe { DynChannelAccess::::conjure(ch_idx) }; + on_rx(raw); + } + return; + } + } + } } - impl ChannelInternal for A - where - A: RawChannelAccess, - { + impl DynChannelAccess { #[inline] - fn update(&self) { + pub fn channel(&self) -> u8 { + self.ch_idx as u8 + } + + #[inline] + pub fn update(&self) { // no-op } - fn set_divider(&self, divider: u8) { + pub fn set_divider(&self, divider: u8) { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf0(self.channel() as usize) + rmt.chconf0(self.ch_idx as usize) .modify(|_, w| unsafe { w.div_cnt().bits(divider) }); } #[inline] - fn memsize(&self) -> MemSize { + pub fn memsize(&self) -> MemSize { let rmt = crate::peripherals::RMT::regs(); - let blocks = rmt - .chconf0(self.channel() as usize) - .read() - .mem_size() - .bits(); + let blocks = rmt.chconf0(self.ch_idx as usize).read().mem_size().bits(); MemSize::from_blocks(blocks) } - fn set_memsize(&self, value: MemSize) { + pub fn set_memsize(&self, value: MemSize) { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf0(self.channel() as usize) + rmt.chconf0(self.ch_idx as usize) .modify(|_, w| unsafe { w.mem_size().bits(value.blocks()) }); } } - impl TxChannelInternal for A - where - A: RawChannelAccess, - { + impl DynChannelAccess { #[cfg(not(esp32))] #[inline] - fn set_generate_repeat_interrupt(&self, repeats: u16) { + pub fn set_generate_repeat_interrupt(&self, repeats: u16) { let rmt = crate::peripherals::RMT::regs(); if repeats > 1 { - rmt.ch_tx_lim(self.channel() as usize) + rmt.ch_tx_lim(self.ch_idx as usize) .modify(|_, w| unsafe { w.tx_loop_num().bits(repeats) }); } else { - rmt.ch_tx_lim(self.channel() as usize) + rmt.ch_tx_lim(self.ch_idx as usize) .modify(|_, w| unsafe { w.tx_loop_num().bits(0) }); } } #[cfg(esp32)] #[inline] - fn set_generate_repeat_interrupt(&self, _repeats: u16) { + pub fn set_generate_repeat_interrupt(&self, _repeats: u16) { // unsupported } #[inline] - fn clear_tx_interrupts(&self) { + pub fn clear_tx_interrupts(&self) { let rmt = crate::peripherals::RMT::regs(); - let ch = self.channel(); + let ch = self.ch_idx as u8; rmt.int_clr().write(|w| { w.ch_err(ch).set_bit(); @@ -2477,23 +2281,23 @@ mod chip_specific { } #[inline] - fn set_tx_continuous(&self, continuous: bool) { + pub fn set_tx_continuous(&self, continuous: bool) { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf1(self.channel() as usize) + rmt.chconf1(self.ch_idx as usize) .modify(|_, w| w.tx_conti_mode().bit(continuous)); } #[inline] - fn set_tx_wrap_mode(&self, wrap: bool) { + pub fn set_tx_wrap_mode(&self, wrap: bool) { let rmt = crate::peripherals::RMT::regs(); // this is "okay", because we use all TX channels always in wrap mode rmt.apb_conf().modify(|_, w| w.mem_tx_wrap_en().bit(wrap)); } - fn set_tx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level) { + pub fn set_tx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level) { let rmt = crate::peripherals::RMT::regs(); - let ch = self.channel() as usize; + let ch = self.ch_idx as usize; rmt.chcarrier_duty(ch) .write(|w| unsafe { w.carrier_high().bits(high).carrier_low().bits(low) }); @@ -2506,16 +2310,16 @@ mod chip_specific { }); } - fn set_tx_idle_output(&self, enable: bool, level: Level) { + pub fn set_tx_idle_output(&self, enable: bool, level: Level) { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf1(self.channel() as usize) + rmt.chconf1(self.ch_idx as usize) .modify(|_, w| w.idle_out_en().bit(enable).idle_out_lv().bit(level.into())); } #[inline] - fn start_tx(&self) { + pub fn start_tx(&self) { let rmt = crate::peripherals::RMT::regs(); - let ch = self.channel(); + let ch = self.ch_idx as u8; for i in 1..self.memsize().blocks() { rmt.chconf1((ch + i).into()) @@ -2530,11 +2334,13 @@ mod chip_specific { }); } + // Return the first flag that is set of, in order of decreasing priority, + // Event::Error, Event::End, Event::Threshold #[inline] - fn get_tx_status(&self) -> Option { + pub fn get_tx_status(&self) -> Option { let rmt = crate::peripherals::RMT::regs(); let reg = rmt.int_raw().read(); - let ch = self.channel(); + let ch = self.ch_idx as u8; if reg.ch_tx_end(ch).bit() { Some(Event::End) @@ -2548,41 +2354,43 @@ mod chip_specific { } #[inline] - fn reset_tx_threshold_set(&self) { + pub fn reset_tx_threshold_set(&self) { let rmt = crate::peripherals::RMT::regs(); rmt.int_clr() - .write(|w| w.ch_tx_thr_event(self.channel()).set_bit()); + .write(|w| w.ch_tx_thr_event(self.ch_idx as u8).set_bit()); } #[inline] - fn set_tx_threshold(&self, threshold: u8) { + pub fn set_tx_threshold(&self, threshold: u8) { let rmt = crate::peripherals::RMT::regs(); - rmt.ch_tx_lim(self.channel() as usize) + rmt.ch_tx_lim(self.ch_idx as usize) .modify(|_, w| unsafe { w.tx_lim().bits(threshold as u16) }); } #[inline] - fn is_tx_loopcount_interrupt_set(&self) -> bool { + pub fn is_tx_loopcount_interrupt_set(&self) -> bool { // no-op false } #[cfg(esp32s2)] + #[allow(unused)] #[inline] - fn stop_tx(&self) { + pub fn stop_tx(&self) { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf1(self.channel() as usize) + rmt.chconf1(self.ch_idx as usize) .modify(|_, w| w.tx_stop().set_bit()); } #[cfg(esp32)] + #[allow(unused)] #[inline] - fn stop_tx(&self) {} + pub fn stop_tx(&self) {} #[inline] - fn set_tx_interrupt(&self, events: EnumSet, enable: bool) { + pub fn set_tx_interrupt(&self, events: EnumSet, enable: bool) { let rmt = crate::peripherals::RMT::regs(); - let ch = self.channel(); + let ch = self.ch_idx as u8; rmt.int_ena().modify(|_, w| { if events.contains(Event::Error) { w.ch_err(ch).bit(enable); @@ -2598,14 +2406,11 @@ mod chip_specific { } } - impl RxChannelInternal for A - where - A: RawChannelAccess, - { + impl DynChannelAccess { #[inline] - fn clear_rx_interrupts(&self) { + pub fn clear_rx_interrupts(&self) { let rmt = crate::peripherals::RMT::regs(); - let ch = self.channel(); + let ch = self.ch_idx as u8; rmt.int_clr().write(|w| { w.ch_rx_end(ch).set_bit(); @@ -2615,13 +2420,13 @@ mod chip_specific { } #[inline] - fn set_rx_wrap_mode(&self, _wrap: bool) { + pub fn set_rx_wrap_mode(&self, _wrap: bool) { // no-op } - fn set_rx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level) { + pub fn set_rx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level) { let rmt = crate::peripherals::RMT::regs(); - let ch = self.channel() as usize; + let ch = self.ch_idx as usize; rmt.chcarrier_duty(ch) .write(|w| unsafe { w.carrier_high().bits(high).carrier_low().bits(low) }); @@ -2635,9 +2440,9 @@ mod chip_specific { } #[inline] - fn start_rx(&self) { + pub fn start_rx(&self) { let rmt = crate::peripherals::RMT::regs(); - let ch = self.channel(); + let ch = self.ch_idx as u8; for i in 1..self.memsize().blocks() { rmt.chconf1((ch + i).into()) @@ -2652,11 +2457,13 @@ mod chip_specific { }); } + // Return the first flag that is set of, in order of decreasing priority, + // Event::Error, Event::End, Event::Threshold #[inline] - fn get_rx_status(&self) -> Option { + pub fn get_rx_status(&self) -> Option { let rmt = crate::peripherals::RMT::regs(); let reg = rmt.int_raw().read(); - let ch = self.channel(); + let ch = self.ch_idx as u8; if reg.ch_rx_end(ch).bit() { Some(Event::End) @@ -2668,30 +2475,30 @@ mod chip_specific { } #[inline] - fn stop_rx(&self) { + pub fn stop_rx(&self) { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf1(self.channel() as usize) + rmt.chconf1(self.ch_idx as usize) .modify(|_, w| w.rx_en().clear_bit()); } - fn set_rx_filter_threshold(&self, value: u8) { + pub fn set_rx_filter_threshold(&self, value: u8) { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf1(self.channel() as usize).modify(|_, w| unsafe { + rmt.chconf1(self.ch_idx as usize).modify(|_, w| unsafe { w.rx_filter_en().bit(value > 0); w.rx_filter_thres().bits(value) }); } - fn set_rx_idle_threshold(&self, value: u16) { + pub fn set_rx_idle_threshold(&self, value: u16) { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf0(self.channel() as usize) + rmt.chconf0(self.ch_idx as usize) .modify(|_, w| unsafe { w.idle_thres().bits(value) }); } #[inline] - fn set_rx_interrupt(&self, events: EnumSet, enable: bool) { + pub fn set_rx_interrupt(&self, events: EnumSet, enable: bool) { let rmt = crate::peripherals::RMT::regs(); - let ch = self.channel(); + let ch = self.ch_idx as u8; rmt.int_ena().modify(|_, w| { if events.contains(Event::Error) { w.ch_err(ch).bit(enable); diff --git a/examples/async/embassy_rmt_rx/src/main.rs b/examples/async/embassy_rmt_rx/src/main.rs index 0b7bdb00a..e4115180c 100644 --- a/examples/async/embassy_rmt_rx/src/main.rs +++ b/examples/async/embassy_rmt_rx/src/main.rs @@ -11,7 +11,7 @@ use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ gpio::{Level, Output, OutputConfig}, - rmt::{PulseCode, Rmt, RxChannelAsync, RxChannelConfig, RxChannelCreator}, + rmt::{PulseCode, Rmt, RxChannelConfig, RxChannelCreator}, time::Rate, timer::timg::TimerGroup, }; diff --git a/examples/async/embassy_rmt_tx/src/main.rs b/examples/async/embassy_rmt_tx/src/main.rs index dabcec558..6c8a11d8a 100644 --- a/examples/async/embassy_rmt_tx/src/main.rs +++ b/examples/async/embassy_rmt_tx/src/main.rs @@ -13,7 +13,7 @@ use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ gpio::Level, - rmt::{PulseCode, Rmt, TxChannelAsync, TxChannelConfig, TxChannelCreator}, + rmt::{PulseCode, Rmt, TxChannelConfig, TxChannelCreator}, time::Rate, timer::timg::TimerGroup, }; diff --git a/hil-test/tests/rmt.rs b/hil-test/tests/rmt.rs index 004595827..6e0e9e05d 100644 --- a/hil-test/tests/rmt.rs +++ b/hil-test/tests/rmt.rs @@ -7,16 +7,18 @@ #![no_main] use esp_hal::{ + Blocking, DriverMode, gpio::{InputPin, Level, NoPin, OutputPin}, rmt::{ - AnyRxChannel, - AnyTxChannel, + Channel, Error, PulseCode, Rmt, + Rx, RxChannelConfig, RxChannelCreator, + Tx, TxChannelConfig, TxChannelCreator, }, @@ -40,12 +42,11 @@ fn setup( tx: impl OutputPin, tx_config: TxChannelConfig, rx_config: RxChannelConfig, -) -> (AnyTxChannel, AnyRxChannel) { +) -> (Channel, Channel) { let tx_channel = rmt .channel0 .configure_tx(tx, tx_config.with_clk_divider(DIV)) - .unwrap() - .degrade(); + .unwrap(); cfg_if::cfg_if! { if #[cfg(any(esp32, esp32s3))] { @@ -56,8 +57,7 @@ fn setup( }; let rx_channel = rx_channel_creator .configure_rx(rx, rx_config.with_clk_divider(DIV)) - .unwrap() - .degrade(); + .unwrap(); (tx_channel, rx_channel) } @@ -75,22 +75,10 @@ fn generate_tx_data(write_end_marker: bool) -> [PulseCode; tx_data } -// Run a test where some data is sent from one channel and looped back to -// another one for receive, and verify that the data matches. -fn do_rmt_loopback(tx_memsize: u8, rx_memsize: u8) { - use esp_hal::rmt::{RxChannel, TxChannel}; - - let peripherals = esp_hal::init(esp_hal::Config::default()); - let (rx, tx) = hil_test::common_test_pins!(peripherals); - let rmt = Rmt::new(peripherals.RMT, FREQ).unwrap(); - - let tx_config = TxChannelConfig::default().with_memsize(tx_memsize); - let rx_config = RxChannelConfig::default() - .with_idle_threshold(1000) - .with_memsize(rx_memsize); - - let (tx_channel, rx_channel) = setup(rmt, rx, tx, tx_config, rx_config); - +fn do_rmt_loopback_inner( + tx_channel: Channel, + rx_channel: Channel, +) { let tx_data: [_; TX_LEN] = generate_tx_data(true); let mut rcv_data: [PulseCode; TX_LEN] = [PulseCode::default(); TX_LEN]; @@ -115,9 +103,24 @@ fn do_rmt_loopback(tx_memsize: u8, rx_memsize: u8) { // Run a test where some data is sent from one channel and looped back to // another one for receive, and verify that the data matches. -async fn do_rmt_loopback_async(tx_memsize: u8, rx_memsize: u8) { - use esp_hal::rmt::{RxChannelAsync, TxChannelAsync}; +fn do_rmt_loopback(tx_memsize: u8, rx_memsize: u8) { + let peripherals = esp_hal::init(esp_hal::Config::default()); + let (rx, tx) = hil_test::common_test_pins!(peripherals); + let rmt = Rmt::new(peripherals.RMT, FREQ).unwrap(); + let tx_config = TxChannelConfig::default().with_memsize(tx_memsize); + let rx_config = RxChannelConfig::default() + .with_idle_threshold(1000) + .with_memsize(rx_memsize); + + let (tx_channel, rx_channel) = setup(rmt, rx, tx, tx_config, rx_config); + + do_rmt_loopback_inner::(tx_channel, rx_channel); +} + +// Run a test where some data is sent from one channel and looped back to +// another one for receive, and verify that the data matches. +async fn do_rmt_loopback_async(tx_memsize: u8, rx_memsize: u8) { let peripherals = esp_hal::init(esp_hal::Config::default()); let (rx, tx) = hil_test::common_test_pins!(peripherals); let rmt = Rmt::new(peripherals.RMT, FREQ).unwrap().into_async(); @@ -152,8 +155,6 @@ fn do_rmt_single_shot( tx_memsize: u8, write_end_marker: bool, ) -> Result<(), Error> { - use esp_hal::rmt::TxChannel; - let peripherals = esp_hal::init(esp_hal::Config::default()); let (rx, tx) = hil_test::common_test_pins!(peripherals); let rmt = Rmt::new(peripherals.RMT, FREQ).unwrap(); @@ -281,4 +282,92 @@ mod tests { .configure_tx(NoPin, TxChannelConfig::default()) .unwrap(); } + + macro_rules! test_channel_pair { + ( + $peripherals:ident, + $tx_pin:ident, + $rx_pin:ident, + $tx_channel:ident, + $rx_channel:ident + ) => { + // It's currently not possible to reborrow ChannelCreators and reconfigure channels, so + // we reconfigure the whole peripheral for each sub-test. + let rmt = Rmt::new($peripherals.RMT.reborrow(), FREQ).unwrap(); + + let tx_config = TxChannelConfig::default(); + let rx_config = RxChannelConfig::default().with_idle_threshold(1000); + + let tx_channel = rmt + .$tx_channel + .configure_tx($tx_pin.reborrow(), tx_config) + .unwrap(); + + let rx_channel = rmt + .$rx_channel + .configure_rx($rx_pin.reborrow(), rx_config) + .unwrap(); + + do_rmt_loopback_inner::<20>(tx_channel, rx_channel); + }; + } + + // A simple test that uses all channels: This is useful to verify that all channel/register + // indexing in the driver is correct. The other tests are prone to hide related issues due to + // using low channel numbers only (in particular 0 for the tx channel). + #[test] + fn rmt_use_all_channels() { + let mut p = esp_hal::init(esp_hal::Config::default()); + let (mut rx, mut tx) = hil_test::common_test_pins!(p); + + // FIXME: Find a way to implement these with less boilerplate and without a macro. + // Maybe use ChannelCreator::steal() (doesn't help right now because it uses a const + // generic channel number)? + + // Chips with combined RxTx channels + #[cfg(esp32)] + { + test_channel_pair!(p, tx, rx, channel0, channel1); + test_channel_pair!(p, tx, rx, channel0, channel2); + test_channel_pair!(p, tx, rx, channel0, channel3); + test_channel_pair!(p, tx, rx, channel0, channel4); + test_channel_pair!(p, tx, rx, channel0, channel5); + test_channel_pair!(p, tx, rx, channel0, channel6); + test_channel_pair!(p, tx, rx, channel0, channel7); + + test_channel_pair!(p, tx, rx, channel1, channel0); + test_channel_pair!(p, tx, rx, channel2, channel0); + test_channel_pair!(p, tx, rx, channel3, channel0); + test_channel_pair!(p, tx, rx, channel4, channel0); + test_channel_pair!(p, tx, rx, channel5, channel0); + test_channel_pair!(p, tx, rx, channel6, channel0); + test_channel_pair!(p, tx, rx, channel7, channel0); + } + + #[cfg(esp32s2)] + { + test_channel_pair!(p, tx, rx, channel0, channel1); + test_channel_pair!(p, tx, rx, channel0, channel2); + test_channel_pair!(p, tx, rx, channel0, channel3); + + test_channel_pair!(p, tx, rx, channel1, channel0); + test_channel_pair!(p, tx, rx, channel2, channel0); + test_channel_pair!(p, tx, rx, channel3, channel0); + } + + // Chips with separate Rx and Tx channels + #[cfg(esp32s3)] + { + test_channel_pair!(p, tx, rx, channel0, channel4); + test_channel_pair!(p, tx, rx, channel1, channel5); + test_channel_pair!(p, tx, rx, channel2, channel6); + test_channel_pair!(p, tx, rx, channel3, channel7); + } + + #[cfg(not(any(esp32, esp32s2, esp32s3)))] + { + test_channel_pair!(p, tx, rx, channel0, channel2); + test_channel_pair!(p, tx, rx, channel1, channel3); + } + } }