diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 99cf3b2afa..3d60c32d84 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -8,14 +8,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- All public RMT types now derive `Debug`. (#4302) ### Changed - `work_queue` is no longer public (#4357) - UART memory is now powered down when the driver is no longer in use. (#4354) +- `rmt::SingleShotTxTransaction` has been renamed to `rmt::TxTransaction`. (#4302) +- `rmt::ChannelCreator::configure_tx` and `rmt::ChannelCreator::configure_rx` now take the configuration by reference (#4302) ### Fixed +- RMT: All blocking methods now return the channel on failure (#4302) +- RMT: the `place_rmt_driver_in_ram` option now also places the async interrupt handler in RAM (#4302) ### Removed diff --git a/esp-hal/MIGRATING-1.0.0-rc.0.md b/esp-hal/MIGRATING-1.0.0-rc.0.md index 8cd0cb62a4..eef0822bdd 100644 --- a/esp-hal/MIGRATING-1.0.0-rc.0.md +++ b/esp-hal/MIGRATING-1.0.0-rc.0.md @@ -133,9 +133,9 @@ 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` +`Channel<'_, Dm: DriverMode, Tx>` and -`Channel` +`Channel<'_, Dm: DriverMode, Rx>` respectively and the `RxChannel`, `TxChannel`, `RxChannelAsync` and `TxChannelAsync` traits have been removed. Several related types that were previously exported have been removed from the @@ -145,8 +145,8 @@ API as well. -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<'_, Blocking, Tx> = rmt.channel0.configure_tx(NoPin, TxChannelConfig::default()); ++let mut rx: Channel<'_, Blocking, Rx> = rmt.channel2.configure_rx(NoPin, RxChannelConfig::default()); -let mut tx: Channel> = tx.degrade(); -let mut rx: Channel> = rx.degrade(); @@ -160,20 +160,7 @@ API as well. +let rx_transaction: RxTransaction<'_, PulseCode> = rx.transmit(&data); ``` -### RMT method changes - -The `rmt::Channel::transmit_continuously` and -`rmt::Channel::transmit_continuously_with_loopcount` methods have been merged: - -```diff --let tx_trans0 = tx_channel0.transmit_continuously(&data); --let tx_trans1 = tx_channel1.transmit_continuously_with_loopcount(&data, count); -+use core::num::NonZeroU16; -+use esp_hal::rmt::LoopCount; -+let tx_trans0 = tx_channel0.transmit_continuously(&data, LoopCount::Infinite); -+let count = NonZeroU16::new(count).unwrap(); -+let tx_trans1 = tx_channel1.transmit_continuously(&data, LoopCount::Finite(count)); -``` +### RMT Receiver Changes The receiver methods `rmt::Channel::<'_, Blocking>::receive` and `rmt::Channel::<'_, Async>::receive` now return the actual amount of data read, @@ -208,25 +195,25 @@ Additionally, RMT transaction types - `RxTransaction` - futures returned by the async API are marked as `#[must_use]` to account for the fact that it is in general required to poll them to ensure progress. -Additionally, they now implement `Drop` and stop the ongoing transfer as quickly as possible when dropped, +All of them now implement `Drop` and stop the ongoing transfer as quickly as possible when dropped, ensuring that subsequent transactions start from a well-defined state. ### RMT continuous transmit changes -The behavior of `Channel::transmit_continuously` has been clarified to account -for the varying hardware support between devices: It now takes an additional -`LoopStop` argument. +The `rmt::Channel::transmit_continuously` and +`rmt::Channel::transmit_continuously_with_loopcount` methods have been merged, +and their signature has been modified to account for the varying hardware +support between devices: It now takes an additional `LoopMode` argument, the variants +of which are only defined when the hardware supports them. ```diff -- let transaction = channel.transmit_continuously(&data, LoopCount::Finite(count))?; -+ let transaction = channel.transmit_continuously(&data, LoopCount::Finite(count), LoopStop::Manual)?; +-let tx_trans0 = tx_channel0.transmit_continuously(&data); +-let tx_trans1 = tx_channel1.transmit_continuously_with_loopcount(&data, count); ++use esp_hal::rmt::LoopMode; ++let tx_trans0 = tx_channel0.transmit_continuously(&data, LoopMode::Infinite); ++let tx_trans1 = tx_channel1.transmit_continuously(&data, LoopMode::InfiniteWithInterrupt(count)); ``` -Additionally, some enum variants and methods are only defined when the hardware supports them: - -- `LoopCount::Finite` and `ContinuousTxTransaction::is_tx_loopcount_interrupt_set` (all except ESP32), -- `LoopStop::Auto` (ESP32-C6, ESP32-H2, ESP32-S3). - ## DMA changes DMA buffers now have a `Final` associated type parameter. For the publicly available buffer, this is `Self`, diff --git a/esp-hal/MIGRATING-1.0.0-rc.1.md b/esp-hal/MIGRATING-1.0.0-rc.1.md index f291a073d4..d42498ff70 100644 --- a/esp-hal/MIGRATING-1.0.0-rc.1.md +++ b/esp-hal/MIGRATING-1.0.0-rc.1.md @@ -1 +1,31 @@ # Migration Guide from 1.0.0-rc.1 to {{currentVersion}} + +## RMT Changes + +Several details of the driver structs and their methods have changed: + +- `SingleShotTxTransaction` has been renamed to `TxTransaction`. +- All `Channel` and `ChannelCreator` methods that take their arguments (channels and pins) by value now return them on failure; this might require changed to error handling. Changed methods are + - `ChannelCreator<'_, _, _>::configure_tx` and `ChannelCreator<'_, _, _>::configure_rx`, + - `Channel<'_, Blocking, Tx>::transmit`, + - `Channel<'_, Blocking, Tx>::transmit_continuously`, + - `Channel<'_, Blocking, Rx>::receive`. +- The `ChannelCreator::configure_tx` and `ChannelCreator::configure_rx` methods now take the configuration by reference. + +```diff +-let mut tx = rmt.channel0.configure_tx(tx_pin, TxChannelConfig::default()); +-let mut rx = rmt.channel2.configure_rx(rx_pin, RxChannelConfig::default()); ++let mut tx = rmt.channel0.configure_tx(tx_pin, &TxChannelConfig::default()); ++let mut rx = rmt.channel2.configure_rx(rx_pin, &RxChannelConfig::default()); + +-let tx_transaction: SingleShotTxTransaction<'_, PulseCode> = tx.transmit(&data)?; ++let tx_transaction: TxTransaction<'_, PulseCode> = match tx.transmit(&data) { ++ Ok(t) => t, ++ Err((e, channel)) => { ++ // Now, the `channel` is not lost permanently, but can continue to be used ++ // (e.g. to retry configuration with different parameters) ++ e? ++ } ++}; +``` + diff --git a/esp-hal/src/rmt.rs b/esp-hal/src/rmt.rs index 594a254d77..a0390c5a27 100644 --- a/esp-hal/src/rmt.rs +++ b/esp-hal/src/rmt.rs @@ -4,9 +4,9 @@ _ => "let freq = Rate::from_mhz(80);" }, "channel" => { - cfg(any(esp32, esp32s2)) => "let mut channel = rmt.channel0.configure_rx(peripherals.GPIO4, rx_config)?;", - cfg(esp32s3) => "let mut channel = rmt.channel7.configure_rx(peripherals.GPIO4, rx_config)?;", - _ => "let mut channel = rmt.channel2.configure_rx(peripherals.GPIO4, rx_config)?;" + cfg(any(esp32, esp32s2)) => "let mut channel = rmt.channel0.configure_rx(peripherals.GPIO4, &rx_config)?;", + cfg(esp32s3) => "let mut channel = rmt.channel7.configure_rx(peripherals.GPIO4, &rx_config)?;", + _ => "let mut channel = rmt.channel2.configure_rx(peripherals.GPIO4, &rx_config)?;" }, "channels_desc" => { cfg(esp32) => "8 channels, each of them can be either receiver or transmitter.", @@ -64,7 +64,7 @@ //! let rmt = Rmt::new(peripherals.RMT, freq)?; //! let mut channel = rmt.channel0.configure_tx( //! peripherals.GPIO1, -//! TxChannelConfig::default() +//! &TxChannelConfig::default() //! .with_clk_divider(1) //! .with_idle_output_level(Level::Low) //! .with_idle_output(false) @@ -89,7 +89,7 @@ //! //! let tx_config = TxChannelConfig::default().with_clk_divider(255); //! -//! let mut channel = rmt.channel0.configure_tx(peripherals.GPIO4, tx_config)?; +//! let mut channel = rmt.channel0.configure_tx(peripherals.GPIO4, &tx_config)?; //! //! let delay = Delay::new(); //! @@ -711,7 +711,7 @@ for_each_rmt_channel!( where Dm: crate::DriverMode, { - pub(super) peripheral: RMT<'rmt>, + peripheral: RMT<'rmt>, $( #[doc = concat!("RMT Channel ", $num)] pub []: ChannelCreator<'rmt, Dm, $num>, @@ -734,6 +734,21 @@ for_each_rmt_channel!( } } + impl<'rmt> Rmt<'rmt, Blocking> { + /// Reconfigures the driver for asynchronous operation. + pub fn into_async(mut self) -> Rmt<'rmt, Async> { + self.set_interrupt_handler(chip_specific::async_interrupt_handler); + + Rmt { + peripheral: self.peripheral, + $( + []: unsafe { self.[].into_async() }, + )+ + _mode: PhantomData, + } + } + } + #[allow(clippy::no_effect)] const NUM_CHANNELS: usize = const { 0 $( + {$num; 1} )+ }; } @@ -762,10 +777,12 @@ for_each_rmt_channel!( )+ } - #[allow(clippy::no_effect)] - const CHANNEL_INDEX_COUNT: u8 = const { 0 $( + {$idx; 1} )+ }; + impl ChannelIndex { + #[allow(clippy::no_effect)] + const MAX: u8 = const { 0 $( + {$idx; 1} )+ }; + } - const OUTPUT_SIGNALS: [gpio::OutputSignal; CHANNEL_INDEX_COUNT as usize] = [ + const OUTPUT_SIGNALS: [gpio::OutputSignal; ChannelIndex::MAX as usize] = [ $( gpio::OutputSignal::[], )+ @@ -776,20 +793,29 @@ for_each_rmt_channel!( where Dm: crate::DriverMode, { - fn configure_tx( + fn configure_tx

( self, - pin: impl PeripheralOutput<'ch>, - config: TxChannelConfig, - ) -> Result, Error> + pin: P, + config: &TxChannelConfig, + ) -> Result, (Error, Self, P)> where Self: Sized, + P: PeripheralOutput<'ch>, { - unsafe { Channel::configure_tx( - ChannelIndex::[], - pin.into(), - config, - self._guard, - ) } + let raw = unsafe { DynChannelAccess::conjure(ChannelIndex::[]) }; + + let memsize = MemSize::from_blocks(config.memsize); + if let Err(e) = reserve_channel(raw.channel(), RmtState::Tx, memsize) { + return Err((e, self, pin)); + }; + + let _guard = unsafe { configure_tx(raw, memsize, pin.into(), config, self._guard) }; + + Ok(Channel { + raw, + _rmt: core::marker::PhantomData, + _guard, + }) } } )+ @@ -798,7 +824,7 @@ for_each_rmt_channel!( (rx $(($num:literal, $idx:literal)),+) => { paste::paste! { - const INPUT_SIGNALS: [gpio::InputSignal; CHANNEL_INDEX_COUNT as usize] = [ + const INPUT_SIGNALS: [gpio::InputSignal; ChannelIndex::MAX as usize] = [ $( gpio::InputSignal::[], )+ @@ -809,20 +835,34 @@ for_each_rmt_channel!( where Dm: crate::DriverMode, { - fn configure_rx( + fn configure_rx

( self, - pin: impl PeripheralInput<'ch>, - config: RxChannelConfig, - ) -> Result, Error> + pin: P, + config: &RxChannelConfig, + ) -> Result, (Error, Self, P)> where Self: Sized, + P: PeripheralInput<'ch>, { - unsafe { Channel::configure_rx( - ChannelIndex::[], - pin.into(), - config, - self._guard, - ) } + let raw = unsafe { DynChannelAccess::conjure(ChannelIndex::[]) }; + + #[cfg_attr(any(esp32, esp32s2), allow(clippy::absurd_extreme_comparisons))] + if config.idle_threshold > MAX_RX_IDLE_THRESHOLD { + return Err((Error::InvalidArgument, self, pin)); + } + + let memsize = MemSize::from_blocks(config.memsize); + if let Err(e) = reserve_channel(raw.channel(), RmtState::Rx, memsize) { + return Err((e, self, pin)); + }; + + let _guard = unsafe { configure_rx(raw, memsize, pin.into(), config, self._guard) }; + + Ok(Channel { + raw, + _rmt: core::marker::PhantomData, + _guard, + }) } } )+ @@ -837,7 +877,7 @@ impl Iterator for ChannelIndexIter { fn next(&mut self) -> Option { let ch_idx = self.0; - if ch_idx < CHANNEL_INDEX_COUNT { + if ch_idx < ChannelIndex::MAX { self.0 = ch_idx + 1; Some(unsafe { ChannelIndex::from_u8_unchecked(ch_idx) }) } else { @@ -852,7 +892,7 @@ impl ChannelIndex { } unsafe fn from_u8_unchecked(ch_idx: u8) -> Self { - debug_assert!(ch_idx < CHANNEL_INDEX_COUNT); + debug_assert!(ch_idx < ChannelIndex::MAX); unsafe { core::mem::transmute(ch_idx) } } } @@ -865,12 +905,6 @@ impl<'rmt> Rmt<'rmt, Blocking> { Ok(this) } - /// Reconfigures the driver for asynchronous operation. - pub fn into_async(mut self) -> Rmt<'rmt, Async> { - self.set_interrupt_handler(chip_specific::async_interrupt_handler); - Rmt::create(self.peripheral) - } - /// Registers an interrupt handler for the RMT peripheral. /// /// Note that this will replace any previously registered interrupt @@ -899,6 +933,7 @@ impl crate::interrupt::InterruptConfigurable for Rmt<'_, Blocking> { // If this is not possible (because a preceding channel is using the RAM, or // because subsequent channels are in use so that we can't reserve the RAM), // restore all state and return with an error. +#[inline(never)] fn reserve_channel(channel: u8, state: RmtState, memsize: MemSize) -> Result<(), Error> { if memsize.blocks() == 0 || memsize.blocks() > NUM_CHANNELS as u8 - channel { return Err(Error::InvalidMemsize); @@ -1065,95 +1100,62 @@ pub const CHANNEL_RAM_SIZE: usize = property!("rmt.channel_ram_size"); /// Whether the channel supports wrapping rx (wrapping tx is supported on all devices) pub const HAS_RX_WRAP: bool = property!("rmt.has_rx_wrap"); -impl<'ch, Dm> Channel<'ch, Dm, Tx> -where - Dm: crate::DriverMode, -{ - unsafe fn configure_tx( - ch_idx: ChannelIndex, - pin: gpio::interconnect::OutputSignal<'ch>, - config: TxChannelConfig, - guard: Option>, - ) -> Result { - let raw = unsafe { DynChannelAccess::conjure(ch_idx) }; - - let memsize = MemSize::from_blocks(config.memsize); - reserve_channel(raw.channel(), RmtState::Tx, memsize)?; - - pin.apply_output_config(&OutputConfig::default()); - pin.set_output_enable(true); - - raw.output_signal().connect_to(&pin); - - raw.set_divider(config.clk_divider); - raw.set_tx_carrier( - config.carrier_modulation, - config.carrier_high, - config.carrier_low, - config.carrier_level, - ); - raw.set_tx_idle_output(config.idle_output, config.idle_output_level); - raw.set_memsize(memsize); - - let _guard = match guard { - Some(g) => DropState::MemAndGuard(g), - None => DropState::MemoryOnly, - }; - - Ok(Self { - raw, - _rmt: core::marker::PhantomData, - _guard, - }) +#[inline(never)] +unsafe fn configure_tx( + raw: DynChannelAccess, + memsize: MemSize, + pin: gpio::interconnect::OutputSignal<'_>, + config: &TxChannelConfig, + guard: Option>, +) -> DropState { + pin.apply_output_config(&OutputConfig::default()); + pin.set_output_enable(true); + + raw.output_signal().connect_to(&pin); + + raw.set_divider(config.clk_divider); + raw.set_tx_carrier( + config.carrier_modulation, + config.carrier_high, + config.carrier_low, + config.carrier_level, + ); + raw.set_tx_idle_output(config.idle_output, config.idle_output_level); + raw.set_memsize(memsize); + + match guard { + Some(g) => DropState::MemAndGuard(g), + None => DropState::MemoryOnly, } } -impl<'ch, Dm> Channel<'ch, Dm, Rx> -where - Dm: crate::DriverMode, -{ - unsafe fn configure_rx( - ch_idx: ChannelIndex, - pin: gpio::interconnect::InputSignal<'ch>, - config: RxChannelConfig, - guard: Option>, - ) -> Result { - let raw = unsafe { DynChannelAccess::conjure(ch_idx) }; - - #[cfg_attr(any(esp32, esp32s2), allow(clippy::absurd_extreme_comparisons))] - if config.idle_threshold > MAX_RX_IDLE_THRESHOLD { - return Err(Error::InvalidArgument); - } - - let memsize = MemSize::from_blocks(config.memsize); - reserve_channel(raw.channel(), RmtState::Rx, memsize)?; - - pin.apply_input_config(&InputConfig::default()); - pin.set_input_enable(true); - - raw.input_signal().connect_to(&pin); - - raw.set_divider(config.clk_divider); - raw.set_rx_carrier( - config.carrier_modulation, - config.carrier_high, - config.carrier_low, - config.carrier_level, - ); - raw.set_rx_filter_threshold(config.filter_threshold); - raw.set_rx_idle_threshold(config.idle_threshold); - raw.set_memsize(memsize); - - let _guard = match guard { - Some(g) => DropState::MemAndGuard(g), - None => DropState::MemoryOnly, - }; - - Ok(Self { - raw, - _rmt: core::marker::PhantomData, - _guard, - }) +#[inline(never)] +unsafe fn configure_rx( + raw: DynChannelAccess, + memsize: MemSize, + pin: gpio::interconnect::InputSignal<'_>, + config: &RxChannelConfig, + guard: Option>, +) -> DropState { + pin.apply_input_config(&InputConfig::default()); + pin.set_input_enable(true); + + raw.input_signal().connect_to(&pin); + + raw.set_divider(config.clk_divider); + raw.set_rx_carrier( + config.carrier_modulation, + config.carrier_high, + config.carrier_low, + config.carrier_level, + ); + raw.set_rx_filter_threshold(config.filter_threshold); + raw.set_rx_idle_threshold(config.idle_threshold); + raw.set_memsize(memsize); + + match guard { + Some(g) => DropState::MemAndGuard(g), + None => DropState::MemoryOnly, } } @@ -1211,13 +1213,14 @@ where Dm: crate::DriverMode, { /// Configure the TX channel - fn configure_tx( + fn configure_tx

( self, - pin: impl PeripheralOutput<'ch>, - config: TxChannelConfig, - ) -> Result, Error> + pin: P, + config: &TxChannelConfig, + ) -> Result, (Error, Self, P)> where - Self: Sized; + Self: Sized, + P: PeripheralOutput<'ch>; } /// Creates a RX channel @@ -1226,13 +1229,14 @@ where Dm: crate::DriverMode, { /// Configure the RX channel - fn configure_rx( + fn configure_rx

( self, - pin: impl PeripheralInput<'ch>, - config: RxChannelConfig, - ) -> Result, Error> + pin: P, + config: &RxChannelConfig, + ) -> Result, (Error, Self, P)> where - Self: Sized; + Self: Sized, + P: PeripheralInput<'ch>; } /// An in-progress transaction for a single shot TX transaction. @@ -1241,7 +1245,8 @@ where /// `.wait()` needs to be called before the entire buffer has been sent to avoid /// underruns. #[must_use = "transactions need to be `poll()`ed / `wait()`ed for to ensure progress"] -pub struct SingleShotTxTransaction<'ch, 'data, T> +#[derive(Debug)] +pub struct TxTransaction<'ch, 'data, T> where T: Into + Copy, { @@ -1253,7 +1258,7 @@ where remaining_data: &'data [T], } -impl<'ch, T> SingleShotTxTransaction<'ch, '_, T> +impl<'ch, T> TxTransaction<'ch, '_, T> where T: Into + Copy, { @@ -1263,7 +1268,7 @@ where let status = raw.get_tx_status(); if status == Some(Event::Threshold) { - raw.reset_tx_threshold_set(); + raw.clear_tx_interrupts(Event::Threshold); // `RmtWriter::write()` is safe to call even if `poll_internal` is called repeatedly // after the data is exhausted since it returns immediately if already done. @@ -1315,7 +1320,7 @@ where // This is safe since we own `this`, and don't access it below. let channel = unsafe { core::ptr::read(&this.channel) }; - raw.clear_tx_interrupts(); + raw.clear_tx_interrupts(EnumSet::all()); match result { Ok(()) => Ok(channel), @@ -1324,7 +1329,7 @@ where } } -impl Drop for SingleShotTxTransaction<'_, '_, T> +impl Drop for TxTransaction<'_, '_, T> where T: Into + Copy, { @@ -1341,12 +1346,13 @@ where } } - raw.clear_tx_interrupts(); + raw.clear_tx_interrupts(EnumSet::all()); } } /// An in-progress continuous TX transaction #[must_use = "transactions will be aborted when dropped"] +#[derive(Debug)] pub struct ContinuousTxTransaction<'ch> { channel: Channel<'ch, Blocking, Tx>, is_running: bool, @@ -1354,7 +1360,7 @@ pub struct ContinuousTxTransaction<'ch> { impl<'ch> ContinuousTxTransaction<'ch> { // FIXME: This interface isn't great, since one cannot use the waiting time until tx is stopped - // for other things! Implement a poll-like interface similar to SingleShotTxTransaction! + // for other things! Implement a poll-like interface similar to TxTransaction! /// Stop transaction when the current iteration ends. #[cfg_attr(place_rmt_driver_in_ram, inline(always))] pub fn stop_next( @@ -1399,7 +1405,7 @@ impl<'ch> ContinuousTxTransaction<'ch> { } } - raw.clear_tx_interrupts(); + raw.clear_tx_interrupts(EnumSet::all()); } // Disable Drop handler since the transaction is stopped already. @@ -1439,11 +1445,12 @@ impl<'ch> Drop for ContinuousTxTransaction<'ch> { } } - raw.clear_tx_interrupts(); + raw.clear_tx_interrupts(EnumSet::all()); } } /// RMT Channel Creator +#[derive(Debug)] pub struct ChannelCreator<'ch, Dm, const CHANNEL: u8> where Dm: crate::DriverMode, @@ -1469,7 +1476,7 @@ where _guard: Option>, } -impl ChannelCreator<'_, Dm, CHANNEL> +impl<'ch, Dm, const CHANNEL: u8> ChannelCreator<'ch, Dm, CHANNEL> where Dm: crate::DriverMode, { @@ -1480,6 +1487,13 @@ where } } + unsafe fn into_async(self) -> ChannelCreator<'ch, Async, CHANNEL> { + ChannelCreator { + _rmt: PhantomData, + _guard: self._guard, + } + } + /// Reborrow this channel creator for a shorter lifetime `'a`. pub fn reborrow<'a>(&'a mut self) -> ChannelCreator<'a, Dm, CHANNEL> { Self { @@ -1549,14 +1563,14 @@ impl LoopMode { /// Channel in TX mode impl<'ch> Channel<'ch, Blocking, Tx> { /// Start transmitting the given pulse code sequence. - /// This returns a [`SingleShotTxTransaction`] which can be used to wait for + /// This returns a [`TxTransaction`] which can be used to wait for /// the transaction to complete and get back the channel for further /// use. #[cfg_attr(place_rmt_driver_in_ram, ram)] pub fn transmit<'data, T>( self, mut data: &'data [T], - ) -> Result, Error> + ) -> Result, (Error, Self)> where T: Into + Copy, { @@ -1564,18 +1578,18 @@ impl<'ch> Channel<'ch, Blocking, Tx> { let memsize = raw.memsize(); match data.last() { - None => return Err(Error::InvalidArgument), + None => return Err((Error::InvalidArgument, self)), Some(&code) if code.into().is_end_marker() => (), - Some(_) => return Err(Error::EndMarkerMissing), + Some(_) => return Err((Error::EndMarkerMissing, self)), } let mut writer = RmtWriter::new(); writer.write(&mut data, raw, true); - raw.clear_tx_interrupts(); + raw.clear_tx_interrupts(EnumSet::all()); raw.start_send(None, memsize); - Ok(SingleShotTxTransaction { + Ok(TxTransaction { channel: self, writer, remaining_data: data, @@ -1599,7 +1613,7 @@ impl<'ch> Channel<'ch, Blocking, Tx> { self, mut data: &[T], mode: LoopMode, - ) -> Result, Error> + ) -> Result, (Error, Self)> where T: Into + Copy, { @@ -1608,13 +1622,13 @@ impl<'ch> Channel<'ch, Blocking, Tx> { #[cfg(rmt_has_tx_loop_count)] if mode.get_count() > MAX_TX_LOOPCOUNT { - return Err(Error::InvalidArgument); + return Err((Error::InvalidArgument, self)); } if data.is_empty() { - return Err(Error::InvalidArgument); + return Err((Error::InvalidArgument, self)); } else if data.len() > memsize.codes() { - return Err(Error::Overflow); + return Err((Error::Overflow, self)); } // We need a separate flag to track whether we actually started the transmitter: The @@ -1631,7 +1645,7 @@ impl<'ch> Channel<'ch, Blocking, Tx> { let mut writer = RmtWriter::new(); writer.write(&mut data, raw, true); - raw.clear_tx_interrupts(); + raw.clear_tx_interrupts(EnumSet::all()); raw.start_send(Some(mode), memsize); } @@ -1644,6 +1658,7 @@ impl<'ch> Channel<'ch, Blocking, Tx> { /// RX transaction instance #[must_use = "transactions need to be `poll()`ed / `wait()`ed for to ensure progress"] +#[derive(Debug)] pub struct RxTransaction<'ch, 'data, T> where T: From, @@ -1680,7 +1695,7 @@ where } #[cfg(rmt_has_rx_wrap)] Some(Event::Threshold) => { - raw.reset_rx_threshold_set(); + raw.clear_rx_interrupts(Event::Threshold); if self.reader.state == ReaderState::Active { self.reader.read(&mut self.data, raw, false); @@ -1725,7 +1740,7 @@ where // This is safe since we own `this`, and don't access it below. let channel = unsafe { core::ptr::read(&this.channel) }; - raw.clear_rx_interrupts(); + raw.clear_rx_interrupts(EnumSet::all()); match result { Ok(total) => Ok((total, channel)), @@ -1744,7 +1759,7 @@ where raw.stop_rx(true); raw.update(); - raw.clear_rx_interrupts(); + raw.clear_rx_interrupts(EnumSet::all()); } } @@ -1759,7 +1774,7 @@ impl<'ch> Channel<'ch, Blocking, Rx> { pub fn receive<'data, T>( self, data: &'data mut [T], - ) -> Result, Error> + ) -> Result, (Error, Self)> where Self: Sized, T: From, @@ -1768,12 +1783,12 @@ impl<'ch> Channel<'ch, Blocking, Rx> { let memsize = raw.memsize(); if !property!("rmt.has_rx_wrap") && data.len() > memsize.codes() { - return Err(Error::InvalidDataLength); + return Err((Error::InvalidDataLength, self)); } let reader = RmtReader::new(); - raw.clear_rx_interrupts(); + raw.clear_rx_interrupts(EnumSet::all()); raw.start_receive(true, memsize); Ok(RxTransaction { @@ -1787,7 +1802,7 @@ impl<'ch> Channel<'ch, Blocking, Rx> { static WAKER: [AtomicWaker; NUM_CHANNELS] = [const { AtomicWaker::new() }; NUM_CHANNELS]; #[must_use = "futures do nothing unless you `.await` or poll them"] -struct RmtTxFuture<'a, T> +struct TxFuture<'a, T> where T: Into + Copy, { @@ -1799,7 +1814,7 @@ where data: &'a [T], } -impl core::future::Future for RmtTxFuture<'_, T> +impl core::future::Future for TxFuture<'_, T> where T: Into + Copy, { @@ -1827,7 +1842,7 @@ where } } Some(Event::Threshold) => { - raw.reset_tx_threshold_set(); + raw.clear_tx_interrupts(Event::Threshold); this.writer.write(&mut this.data, raw, false); @@ -1844,7 +1859,7 @@ where } } -impl Drop for RmtTxFuture<'_, T> +impl Drop for TxFuture<'_, T> where T: Into + Copy, { @@ -1861,7 +1876,7 @@ where } } - raw.clear_tx_interrupts(); + raw.clear_tx_interrupts(EnumSet::all()); } } @@ -1900,7 +1915,7 @@ impl Channel<'_, Async, Tx> { WriterState::Done => false, }; - raw.clear_tx_interrupts(); + raw.clear_tx_interrupts(EnumSet::all()); let mut events = Event::End | Event::Error; if wrap { events |= Event::Threshold; @@ -1909,7 +1924,7 @@ impl Channel<'_, Async, Tx> { raw.start_send(None, memsize); } - RmtTxFuture { + TxFuture { raw, _phantom: PhantomData, writer, @@ -1919,7 +1934,7 @@ impl Channel<'_, Async, Tx> { } #[must_use = "futures do nothing unless you `.await` or poll them"] -struct RmtRxFuture<'a, T> +struct RxFuture<'a, T> where T: From + Unpin, { @@ -1929,7 +1944,7 @@ where data: &'a mut [T], } -impl core::future::Future for RmtRxFuture<'_, T> +impl core::future::Future for RxFuture<'_, T> where T: From + Unpin, { @@ -1958,7 +1973,7 @@ where } #[cfg(rmt_has_rx_wrap)] Some(Event::Threshold) => { - raw.reset_rx_threshold_set(); + raw.clear_rx_interrupts(Event::Threshold); this.reader.read(&mut this.data, raw, false); @@ -1975,7 +1990,7 @@ where } } -impl Drop for RmtRxFuture<'_, T> +impl Drop for RxFuture<'_, T> where T: From + Unpin, { @@ -1985,7 +2000,7 @@ where raw.stop_rx(true); raw.update(); - raw.clear_rx_interrupts(); + raw.clear_rx_interrupts(EnumSet::all()); } } @@ -2008,12 +2023,12 @@ impl Channel<'_, Async, Rx> { if !property!("rmt.has_rx_wrap") && data.len() > memsize.codes() { reader.state = ReaderState::Error(Error::InvalidDataLength); } else { - raw.clear_rx_interrupts(); + raw.clear_rx_interrupts(EnumSet::all()); raw.listen_rx_interrupt(Event::End | Event::Error | Event::Threshold); raw.start_receive(true, memsize); } - RmtRxFuture { + RxFuture { raw, reader, data, @@ -2033,12 +2048,12 @@ enum Event { impl DynChannelAccess

{ #[inline] - fn channel_ram_start_offset(&self) -> usize { + fn channel_ram_start_offset(self) -> usize { usize::from(self.channel()) * property!("rmt.channel_ram_size") } #[inline] - fn channel_ram_start(&self) -> *mut PulseCode { + fn channel_ram_start(self) -> *mut PulseCode { unsafe { (property!("rmt.ram_start") as *mut PulseCode).add(self.channel_ram_start_offset()) } @@ -2046,42 +2061,45 @@ impl DynChannelAccess { } impl DynChannelAccess { - fn output_signal(&self) -> gpio::OutputSignal { + #[inline(always)] + fn output_signal(self) -> gpio::OutputSignal { OUTPUT_SIGNALS[self.ch_idx as usize] } // We could obtain `memsize` via `self.memsize()` here. However, it is already known at all // call sites, and passing it as argument avoids a volatile read that the compiler wouldn't be // able to deduplicate. - #[inline] - fn start_send(&self, loopmode: Option, memsize: MemSize) { + #[inline(always)] + fn start_send(self, loopmode: Option, memsize: MemSize) { self.set_tx_threshold((memsize.codes() / 2) as u8); self.set_tx_continuous(loopmode.is_some()); #[cfg(rmt_has_tx_loop_count)] - self.set_generate_repeat_interrupt(loopmode); + self.set_loopmode(loopmode); self.set_tx_wrap_mode(loopmode.is_none()); self.update(); self.start_tx(); self.update(); } - #[inline] - fn listen_tx_interrupt(&self, event: impl Into>) { + #[inline(always)] + fn listen_tx_interrupt(self, event: impl Into>) { self.set_tx_interrupt(event.into(), true); } - #[inline] - fn unlisten_tx_interrupt(&self, event: impl Into>) { + #[inline(always)] + fn unlisten_tx_interrupt(self, event: impl Into>) { self.set_tx_interrupt(event.into(), false); } } impl DynChannelAccess { - fn input_signal(&self) -> gpio::InputSignal { + #[inline(always)] + fn input_signal(self) -> gpio::InputSignal { INPUT_SIGNALS[self.ch_idx as usize] } - fn start_receive(&self, _wrap: bool, _memsize: MemSize) { + #[inline(always)] + fn start_receive(self, _wrap: bool, _memsize: MemSize) { #[cfg(rmt_has_rx_wrap)] { self.set_rx_threshold((_memsize.codes() / 2) as u16); @@ -2093,13 +2111,13 @@ impl DynChannelAccess { self.update(); } - #[inline] - fn listen_rx_interrupt(&self, event: impl Into>) { + #[inline(always)] + fn listen_rx_interrupt(self, event: impl Into>) { self.set_rx_interrupt(event.into(), true); } - #[inline] - fn unlisten_rx_interrupt(&self, event: impl Into>) { + #[inline(always)] + fn unlisten_rx_interrupt(self, event: impl Into>) { self.set_rx_interrupt(event.into(), false); } } @@ -2118,6 +2136,7 @@ for_each_rmt_clock_source!( (is_boolean) => { impl ClockSource { + #[inline(always)] fn bit(self) -> bool { match (self as u8) { 0 => false, @@ -2130,10 +2149,12 @@ for_each_rmt_clock_source!( (default ($name:ident)) => { impl ClockSource { + #[inline(always)] fn default() -> Self { Self::$name } + #[inline(always)] fn bits(self) -> u8 { self as u8 } @@ -2181,7 +2202,6 @@ mod chip_specific { use enumset::EnumSet; use super::{ - CHANNEL_INDEX_COUNT, ChannelIndex, ClockSource, Direction, @@ -2210,43 +2230,50 @@ mod chip_specific { #[cfg(not(soc_has_pcr))] { - RMT::regs().sys_conf().modify(|_, w| unsafe { - w.clk_en().clear_bit(); - w.sclk_sel().bits(source.bits()); - w.sclk_div_num().bits(div); - w.sclk_div_a().bits(0); - w.sclk_div_b().bits(0); - w.apb_fifo_mask().set_bit() - }); + RMT::regs().sys_conf().modify( + #[inline(always)] + |_, w| unsafe { + w.clk_en().clear_bit(); + w.sclk_sel().bits(source.bits()); + w.sclk_div_num().bits(div); + w.sclk_div_a().bits(0); + w.sclk_div_b().bits(0); + w.apb_fifo_mask().set_bit() + }, + ); } #[cfg(soc_has_pcr)] { use crate::peripherals::PCR; - PCR::regs().rmt_sclk_conf().modify(|_, w| unsafe { - w.sclk_div_num().bits(div); - w.sclk_div_a().bits(0); - w.sclk_div_b().bits(0) - }); - - #[cfg(esp32c6)] - PCR::regs() - .rmt_sclk_conf() - .modify(|_, w| unsafe { w.sclk_sel().bits(source.bits()) }); - #[cfg(not(esp32c6))] - PCR::regs() - .rmt_sclk_conf() - .modify(|_, w| w.sclk_sel().bit(source.bit())); - - RMT::regs() - .sys_conf() - .modify(|_, w| w.apb_fifo_mask().set_bit()); + + PCR::regs().rmt_sclk_conf().modify( + #[inline(always)] + |_, w| unsafe { + cfg_if::cfg_if!( + if #[cfg(esp32c6)] { + w.sclk_sel().bits(source.bits()) + } else { + w.sclk_sel().bit(source.bit()) + } + ); + w.sclk_div_num().bits(div); + w.sclk_div_a().bits(0); + w.sclk_div_b().bits(0) + }, + ); + + RMT::regs().sys_conf().modify( + #[inline(always)] + |_, w| w.apb_fifo_mask().set_bit(), + ); } Ok(()) } #[crate::handler] + #[cfg_attr(place_rmt_driver_in_ram, ram)] pub(super) fn async_interrupt_handler() { let st = RMT::regs().int_st().read(); @@ -2259,14 +2286,14 @@ mod chip_specific { raw_tx.unlisten_tx_interrupt(EnumSet::all()); raw_tx.channel() } else if st.ch_tx_thr_event(ch_idx).bit() { - // RmtTxFuture will enable the interrupt again if required. + // TxFuture will enable the interrupt again if required. raw_tx.unlisten_tx_interrupt(Event::Threshold); raw_tx.channel() } else if st.ch_rx_end(ch_idx).bit() || st.ch_rx_err(ch_idx).bit() { raw_rx.unlisten_rx_interrupt(EnumSet::all()); raw_rx.channel() } else if st.ch_rx_thr_event(ch_idx).bit() { - // RmtRxFuture will enable the interrupt again if required. + // RxFuture will enable the interrupt again if required. raw_rx.unlisten_rx_interrupt(Event::Threshold); raw_rx.channel() } else { @@ -2279,44 +2306,53 @@ mod chip_specific { } impl DynChannelAccess { - #[inline] - pub fn channel(&self) -> u8 { + #[inline(always)] + pub fn channel(self) -> u8 { if Dir::IS_TX { self.ch_idx as u8 } else { - self.ch_idx as u8 + CHANNEL_INDEX_COUNT + self.ch_idx as u8 + ChannelIndex::MAX } } - #[inline] - pub fn update(&self) { + #[inline(always)] + 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()); + rmt.ch_tx_conf0(ch_idx).modify( + #[inline(always)] + |_, w| w.conf_update().set_bit(), + ); } else { - rmt.ch_rx_conf1(ch_idx) - .modify(|_, w| w.conf_update().set_bit()); + rmt.ch_rx_conf1(ch_idx).modify( + #[inline(always)] + |_, w| w.conf_update().set_bit(), + ); } } - pub fn set_divider(&self, divider: u8) { + #[inline(always)] + pub fn set_divider(self, divider: u8) { 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| unsafe { w.div_cnt().bits(divider) }); + rmt.ch_tx_conf0(ch_idx).modify( + #[inline(always)] + |_, w| unsafe { w.div_cnt().bits(divider) }, + ); } else { - rmt.ch_rx_conf0(ch_idx) - .modify(|_, w| unsafe { w.div_cnt().bits(divider) }); + rmt.ch_rx_conf0(ch_idx).modify( + #[inline(always)] + |_, w| unsafe { w.div_cnt().bits(divider) }, + ); } } - #[inline] - pub fn memsize(&self) -> MemSize { + #[inline(always)] + pub fn memsize(self) -> MemSize { let rmt = RMT::regs(); let ch_idx = self.ch_idx as usize; @@ -2329,17 +2365,22 @@ mod chip_specific { MemSize::from_blocks(blocks) } - pub fn set_memsize(&self, value: MemSize) { + #[inline(always)] + pub fn set_memsize(self, value: MemSize) { let blocks = value.blocks(); let rmt = RMT::regs(); let ch_idx = self.ch_idx as usize; if Dir::IS_TX { - rmt.ch_tx_conf0(ch_idx) - .modify(|_, w| unsafe { w.mem_size().bits(blocks) }); + rmt.ch_tx_conf0(ch_idx).modify( + #[inline(always)] + |_, w| unsafe { w.mem_size().bits(blocks) }, + ); } else { - rmt.ch_rx_conf0(ch_idx) - .modify(|_, w| unsafe { w.mem_size().bits(blocks) }); + rmt.ch_rx_conf0(ch_idx).modify( + #[inline(always)] + |_, w| unsafe { w.mem_size().bits(blocks) }, + ); } } } @@ -2350,129 +2391,166 @@ mod chip_specific { max_from_register_spec!(u16, ch_tx_lim, CH_TX_LIM_SPEC, TX_LOOP_NUM_W); impl DynChannelAccess { - #[inline] - pub fn set_generate_repeat_interrupt(&self, mode: Option) { + #[inline(always)] + pub fn set_loopmode(self, mode: Option) { let rmt = crate::peripherals::RMT::regs(); let ch_idx = self.ch_idx as usize; if let Some(mode) = mode { - rmt.ch_tx_lim(ch_idx).modify(|_, w| unsafe { - w.loop_count_reset().set_bit(); - w.tx_loop_cnt_en().bit(!matches!(mode, LoopMode::Infinite)); - w.tx_loop_num().bits(mode.get_count()); + rmt.ch_tx_lim(ch_idx).modify( + #[inline(always)] + |_, w| unsafe { + w.loop_count_reset().set_bit(); + w.tx_loop_cnt_en().bit(!matches!(mode, LoopMode::Infinite)); + w.tx_loop_num().bits(mode.get_count()); - #[cfg(rmt_has_tx_loop_auto_stop)] - w.loop_stop_en().bit(matches!(mode, LoopMode::Finite(_))); + #[cfg(rmt_has_tx_loop_auto_stop)] + w.loop_stop_en().bit(matches!(mode, LoopMode::Finite(_))); - w - }); + w + }, + ); // FIXME: Is this required? This is a WT field for esp32c6 at least - rmt.ch_tx_lim(ch_idx) - .modify(|_, w| w.loop_count_reset().clear_bit()); + rmt.ch_tx_lim(ch_idx).modify( + #[inline(always)] + |_, w| w.loop_count_reset().clear_bit(), + ); } else { - rmt.ch_tx_lim(ch_idx) - .modify(|_, w| w.tx_loop_cnt_en().clear_bit()); + rmt.ch_tx_lim(ch_idx).modify( + #[inline(always)] + |_, w| w.tx_loop_cnt_en().clear_bit(), + ); } } - #[inline] - pub fn clear_tx_interrupts(&self) { + #[inline(always)] + pub fn clear_tx_interrupts(self, events: impl Into>) { let rmt = crate::peripherals::RMT::regs(); + let events = events.into(); + + rmt.int_clr().write( + #[inline(always)] + |w| { + let ch_idx = self.ch_idx as u8; - rmt.int_clr().write(|w| { - w.ch_tx_end(self.channel()).set_bit(); - w.ch_tx_err(self.channel()).set_bit(); - w.ch_tx_loop(self.channel()).set_bit(); - w.ch_tx_thr_event(self.channel()).set_bit() - }); + w.ch_tx_end(ch_idx).bit(events.contains(Event::End)); + w.ch_tx_err(ch_idx).bit(events.contains(Event::Error)); + w.ch_tx_loop(ch_idx).bit(events.contains(Event::LoopCount)); + w.ch_tx_thr_event(ch_idx) + .bit(events.contains(Event::Threshold)); + + w + }, + ); } - #[inline] - pub fn set_tx_continuous(&self, continuous: bool) { + #[inline(always)] + pub fn set_tx_continuous(self, continuous: bool) { let rmt = crate::peripherals::RMT::regs(); + let ch_idx = self.ch_idx as usize; - rmt.ch_tx_conf0(self.channel().into()) - .modify(|_, w| w.tx_conti_mode().bit(continuous)); + rmt.ch_tx_conf0(ch_idx).modify( + #[inline(always)] + |_, w| w.tx_conti_mode().bit(continuous), + ); } - #[inline] - pub fn set_tx_wrap_mode(&self, wrap: bool) { + #[inline(always)] + pub fn set_tx_wrap_mode(self, wrap: bool) { let rmt = crate::peripherals::RMT::regs(); + let ch_idx = self.ch_idx as usize; - rmt.ch_tx_conf0(self.channel().into()) - .modify(|_, w| w.mem_tx_wrap_en().bit(wrap)); + rmt.ch_tx_conf0(ch_idx).modify( + #[inline(always)] + |_, w| w.mem_tx_wrap_en().bit(wrap), + ); } - pub fn set_tx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level) { + #[inline(always)] + pub fn set_tx_carrier(self, carrier: bool, high: u16, low: u16, level: Level) { let rmt = crate::peripherals::RMT::regs(); + let ch_idx = self.ch_idx as usize; - rmt.chcarrier_duty(self.channel().into()) - .write(|w| unsafe { w.carrier_high().bits(high).carrier_low().bits(low) }); + rmt.chcarrier_duty(ch_idx).write( + #[inline(always)] + |w| unsafe { w.carrier_high().bits(high).carrier_low().bits(low) }, + ); - rmt.ch_tx_conf0(self.channel().into()).modify(|_, w| { - w.carrier_en().bit(carrier); - w.carrier_eff_en().set_bit(); - w.carrier_out_lv().bit(level.into()) - }); + rmt.ch_tx_conf0(ch_idx).modify( + #[inline(always)] + |_, w| { + w.carrier_en().bit(carrier); + w.carrier_eff_en().set_bit(); + w.carrier_out_lv().bit(level.into()) + }, + ); } - pub fn set_tx_idle_output(&self, enable: bool, level: Level) { + #[inline(always)] + 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())); + let ch_idx = self.ch_idx as usize; + + rmt.ch_tx_conf0(ch_idx).modify( + #[inline(always)] + |_, w| w.idle_out_en().bit(enable).idle_out_lv().bit(level.into()), + ); } - #[inline] - pub fn start_tx(&self) { + #[inline(always)] + pub fn start_tx(self) { let rmt = crate::peripherals::RMT::regs(); + let ch_idx = self.ch_idx as usize; - rmt.ch_tx_conf0(self.channel().into()).modify(|_, w| { - w.mem_rd_rst().set_bit(); - w.apb_mem_rst().set_bit(); - w.tx_start().set_bit() - }); + rmt.ch_tx_conf0(ch_idx).modify( + #[inline(always)] + |_, w| { + w.mem_rd_rst().set_bit(); + w.apb_mem_rst().set_bit(); + w.tx_start().set_bit() + }, + ); } // Return the first flag that is set of, in order of decreasing priority, // Event::Error, Event::End, Event::LoopCount, Event::Threshold - #[inline] - pub fn get_tx_status(&self) -> Option { + #[inline(always)] + 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_idx = self.ch_idx as u8; - if reg.ch_tx_end(ch).bit() { + if reg.ch_tx_end(ch_idx).bit() { Some(Event::End) - } else if reg.ch_tx_err(ch).bit() { + } else if reg.ch_tx_err(ch_idx).bit() { Some(Event::Error) - } else if reg.ch_tx_loop(ch).bit() { + } else if reg.ch_tx_loop(ch_idx).bit() { Some(Event::LoopCount) - } else if reg.ch_tx_thr_event(ch).bit() { + } else if reg.ch_tx_thr_event(ch_idx).bit() { Some(Event::Threshold) } else { None } } - #[inline] - pub fn reset_tx_threshold_set(&self) { + #[inline(always)] + pub fn set_tx_threshold(self, threshold: u8) { let rmt = crate::peripherals::RMT::regs(); - rmt.int_clr() - .write(|w| w.ch_tx_thr_event(self.channel()).set_bit()); - } + let ch_idx = self.ch_idx as usize; - #[inline] - 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) }); + rmt.ch_tx_lim(ch_idx).modify( + #[inline(always)] + |_, w| unsafe { w.tx_lim().bits(threshold as u16) }, + ); } - #[inline] - pub fn is_tx_loopcount_interrupt_set(&self) -> bool { + #[inline(always)] + 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() + let ch_idx = self.ch_idx as u8; + + rmt.int_raw().read().ch_tx_loop(ch_idx).bit() } // Returns whether stopping was immediate, or needs to wait for tx end. @@ -2480,34 +2558,44 @@ mod chip_specific { // depends on this. // // Requires an update() call - #[inline] - pub fn stop_tx(&self) -> bool { + #[inline(always)] + pub fn stop_tx(self) -> bool { let rmt = crate::peripherals::RMT::regs(); - rmt.ch_tx_conf0(self.channel().into()) - .modify(|_, w| w.tx_stop().set_bit()); + let ch_idx = self.ch_idx as usize; + + rmt.ch_tx_conf0(ch_idx).modify( + #[inline(always)] + |_, w| w.tx_stop().set_bit(), + ); true } - #[inline] - pub fn set_tx_interrupt(&self, events: EnumSet, enable: bool) { + #[inline(always)] + 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) { - w.ch_tx_err(self.channel()).bit(enable); - } - if events.contains(Event::End) { - w.ch_tx_end(self.channel()).bit(enable); - } - if events.contains(Event::Threshold) { - w.ch_tx_thr_event(self.channel()).bit(enable); - } - w - }); + + rmt.int_ena().modify( + #[inline(always)] + |_, w| { + let ch_idx = self.ch_idx as u8; + + if events.contains(Event::Error) { + w.ch_tx_err(ch_idx).bit(enable); + } + if events.contains(Event::End) { + w.ch_tx_end(ch_idx).bit(enable); + } + if events.contains(Event::Threshold) { + w.ch_tx_thr_event(ch_idx).bit(enable); + } + w + }, + ); } #[allow(unused)] - #[inline] - pub fn hw_offset(&self) -> usize { + #[inline(always)] + pub fn hw_offset(self) -> usize { let rmt = crate::peripherals::RMT::regs(); let ch_idx = self.ch_idx as usize; @@ -2522,65 +2610,86 @@ mod chip_specific { max_from_register_spec!(u16, ch_rx_conf0, CH_RX_CONF0_SPEC, IDLE_THRES_W); impl DynChannelAccess { - #[inline] - pub fn clear_rx_interrupts(&self) { + #[inline(always)] + pub fn clear_rx_interrupts(self, events: impl Into>) { let rmt = crate::peripherals::RMT::regs(); - let ch_idx = self.ch_idx as u8; + let events = events.into(); - rmt.int_clr().write(|w| { - w.ch_rx_end(ch_idx).set_bit(); - w.ch_rx_err(ch_idx).set_bit(); - w.ch_rx_thr_event(ch_idx).set_bit() - }); + rmt.int_clr().write( + #[inline(always)] + |w| { + let ch_idx = self.ch_idx as u8; + + w.ch_rx_end(ch_idx).bit(events.contains(Event::End)); + w.ch_rx_err(ch_idx).bit(events.contains(Event::Error)); + w.ch_rx_thr_event(ch_idx) + .bit(events.contains(Event::Threshold)); + w + }, + ); } - #[inline] - pub fn set_rx_wrap_mode(&self, wrap: bool) { + #[inline(always)] + pub fn set_rx_wrap_mode(self, wrap: bool) { let rmt = crate::peripherals::RMT::regs(); let ch_idx = self.ch_idx as usize; - rmt.ch_rx_conf1(ch_idx) - .modify(|_, w| w.mem_rx_wrap_en().bit(wrap)); + rmt.ch_rx_conf1(ch_idx).modify( + #[inline(always)] + |_, w| w.mem_rx_wrap_en().bit(wrap), + ); } - pub fn set_rx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level) { - let ch_idx = self.ch_idx as usize; + #[inline(always)] + pub fn set_rx_carrier(self, carrier: bool, high: u16, low: u16, level: Level) { let rmt = crate::peripherals::RMT::regs(); + let ch_idx = self.ch_idx as usize; - rmt.ch_rx_carrier_rm(ch_idx).write(|w| unsafe { - w.carrier_high_thres().bits(high); - w.carrier_low_thres().bits(low) - }); + rmt.ch_rx_carrier_rm(ch_idx).write( + #[inline(always)] + |w| unsafe { + w.carrier_high_thres().bits(high); + w.carrier_low_thres().bits(low) + }, + ); - rmt.ch_rx_conf0(ch_idx).modify(|_, w| { - w.carrier_en() - .bit(carrier) - .carrier_out_lv() - .bit(level.into()) - }); + rmt.ch_rx_conf0(ch_idx).modify( + #[inline(always)] + |_, w| { + w.carrier_en() + .bit(carrier) + .carrier_out_lv() + .bit(level.into()) + }, + ); } - #[inline] - pub fn start_rx(&self) { + #[inline(always)] + pub fn start_rx(self) { let rmt = crate::peripherals::RMT::regs(); let ch_idx = self.ch_idx as u8; for i in 1..self.memsize().blocks() { - rmt.ch_rx_conf1((ch_idx + i).into()) - .modify(|_, w| w.mem_owner().set_bit()); + rmt.ch_rx_conf1((ch_idx + i).into()).modify( + #[inline(always)] + |_, w| w.mem_owner().set_bit(), + ); } - rmt.ch_rx_conf1(ch_idx.into()).modify(|_, w| { - w.mem_owner().set_bit(); - w.mem_wr_rst().set_bit(); - w.apb_mem_rst().set_bit(); - w.rx_en().set_bit() - }); + rmt.ch_rx_conf1(ch_idx.into()).modify( + #[inline(always)] + |_, w| { + w.mem_owner().set_bit(); + w.mem_wr_rst().set_bit(); + w.apb_mem_rst().set_bit(); + w.rx_en().set_bit() + }, + ); } // Return the first flag that is set of, in order of decreasing priority, // Event::Error, Event::End, Event::Threshold - #[inline] - pub fn get_rx_status(&self) -> Option { + #[inline(always)] + pub fn get_rx_status(self) -> Option { let rmt = crate::peripherals::RMT::regs(); let reg = rmt.int_raw().read(); let ch_idx = self.ch_idx as u8; @@ -2596,70 +2705,82 @@ mod chip_specific { } } - #[inline] - pub fn reset_rx_threshold_set(&self) { + #[inline(always)] + pub fn set_rx_threshold(self, threshold: u16) { let rmt = crate::peripherals::RMT::regs(); - rmt.int_clr() - .write(|w| w.ch_rx_thr_event(self.ch_idx as u8).set_bit()); - } + let ch_idx = self.ch_idx as usize; - #[inline] - pub fn set_rx_threshold(&self, threshold: u16) { - let rmt = crate::peripherals::RMT::regs(); - rmt.ch_rx_lim(self.ch_idx as usize) - .modify(|_, w| unsafe { w.rx_lim().bits(threshold) }); + rmt.ch_rx_lim(ch_idx).modify( + #[inline(always)] + |_, w| unsafe { w.rx_lim().bits(threshold) }, + ); } // This is immediate and does not update state flags; do not poll on get_rx_status() // afterwards! // // Requires an update() call - #[inline] - pub fn stop_rx(&self, _force: bool) { + #[inline(always)] + pub fn stop_rx(self, _force: bool) { let rmt = crate::peripherals::RMT::regs(); let ch_idx = self.ch_idx as usize; - rmt.ch_rx_conf1(ch_idx).modify(|_, w| w.rx_en().clear_bit()); + + rmt.ch_rx_conf1(ch_idx).modify( + #[inline(always)] + |_, w| w.rx_en().clear_bit(), + ); } - pub fn set_rx_filter_threshold(&self, value: u8) { + #[inline(always)] + pub fn set_rx_filter_threshold(self, value: u8) { let rmt = crate::peripherals::RMT::regs(); let ch_idx = self.ch_idx as usize; - rmt.ch_rx_conf1(ch_idx).modify(|_, w| unsafe { - w.rx_filter_en().bit(value > 0); - w.rx_filter_thres().bits(value) - }); + rmt.ch_rx_conf1(ch_idx).modify( + #[inline(always)] + |_, w| unsafe { + w.rx_filter_en().bit(value > 0); + w.rx_filter_thres().bits(value) + }, + ); } - pub fn set_rx_idle_threshold(&self, value: u16) { + #[inline(always)] + pub fn set_rx_idle_threshold(self, value: u16) { let rmt = crate::peripherals::RMT::regs(); let ch_idx = self.ch_idx as usize; - rmt.ch_rx_conf0(ch_idx) - .modify(|_, w| unsafe { w.idle_thres().bits(value) }); + rmt.ch_rx_conf0(ch_idx).modify( + #[inline(always)] + |_, w| unsafe { w.idle_thres().bits(value) }, + ); } - #[inline] - pub fn set_rx_interrupt(&self, events: EnumSet, enable: bool) { + #[inline(always)] + pub fn set_rx_interrupt(self, events: EnumSet, enable: bool) { let rmt = crate::peripherals::RMT::regs(); - let ch_idx = self.ch_idx as u8; - rmt.int_ena().modify(|_, w| { - if events.contains(Event::Error) { - w.ch_rx_err(ch_idx).bit(enable); - } - if events.contains(Event::End) { - w.ch_rx_end(ch_idx).bit(enable); - } - if events.contains(Event::Threshold) { - w.ch_rx_thr_event(ch_idx).bit(enable); - } - w - }); + rmt.int_ena().modify( + #[inline(always)] + |_, w| { + let ch_idx = self.ch_idx as u8; + + if events.contains(Event::Error) { + w.ch_rx_err(ch_idx).bit(enable); + } + if events.contains(Event::End) { + w.ch_rx_end(ch_idx).bit(enable); + } + if events.contains(Event::Threshold) { + w.ch_rx_thr_event(ch_idx).bit(enable); + } + w + }, + ); } - #[inline] - pub fn hw_offset(&self) -> usize { + #[inline(always)] + pub fn hw_offset(self) -> usize { let rmt = crate::peripherals::RMT::regs(); let ch_idx = self.ch_idx as usize; @@ -2697,19 +2818,28 @@ mod chip_specific { let rmt = RMT::regs(); for ch_num in 0..NUM_CHANNELS { - rmt.chconf1(ch_num) - .modify(|_, w| w.ref_always_on().bit(source.bit())); + rmt.chconf1(ch_num).modify( + #[inline(always)] + |_, w| w.ref_always_on().bit(source.bit()), + ); } - rmt.apb_conf().modify(|_, w| w.apb_fifo_mask().set_bit()); + rmt.apb_conf().modify( + #[inline(always)] + |_, w| w.apb_fifo_mask().set_bit(), + ); #[cfg(not(esp32))] - rmt.apb_conf().modify(|_, w| w.clk_en().set_bit()); + rmt.apb_conf().modify( + #[inline(always)] + |_, w| w.clk_en().set_bit(), + ); Ok(()) } #[crate::handler] + #[cfg_attr(place_rmt_driver_in_ram, ram)] pub(super) fn async_interrupt_handler() { let st = RMT::regs().int_st().read(); @@ -2740,33 +2870,45 @@ mod chip_specific { } impl DynChannelAccess { - #[inline] - pub fn channel(&self) -> u8 { + #[inline(always)] + pub fn channel(self) -> u8 { self.ch_idx as u8 } - #[inline] - pub fn update(&self) { + #[inline(always)] + pub fn update(self) { // no-op } - pub fn set_divider(&self, divider: u8) { + #[inline(always)] + pub fn set_divider(self, divider: u8) { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf0(self.ch_idx as usize) - .modify(|_, w| unsafe { w.div_cnt().bits(divider) }); + let ch = self.ch_idx as usize; + + rmt.chconf0(ch).modify( + #[inline(always)] + |_, w| unsafe { w.div_cnt().bits(divider) }, + ); } - #[inline] - pub fn memsize(&self) -> MemSize { + #[inline(always)] + pub fn memsize(self) -> MemSize { let rmt = crate::peripherals::RMT::regs(); - let blocks = rmt.chconf0(self.ch_idx as usize).read().mem_size().bits(); + let ch = self.ch_idx as usize; + + let blocks = rmt.chconf0(ch).read().mem_size().bits(); MemSize::from_blocks(blocks) } - pub fn set_memsize(&self, value: MemSize) { + #[inline(always)] + pub fn set_memsize(self, value: MemSize) { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf0(self.ch_idx as usize) - .modify(|_, w| unsafe { w.mem_size().bits(value.blocks()) }); + let ch = self.ch_idx as usize; + + rmt.chconf0(ch).modify( + #[inline(always)] + |_, w| unsafe { w.mem_size().bits(value.blocks()) }, + ); } } @@ -2778,100 +2920,137 @@ mod chip_specific { impl DynChannelAccess { #[cfg(rmt_has_tx_loop_count)] - #[inline] - pub fn set_generate_repeat_interrupt(&self, mode: Option) { + #[inline(always)] + pub fn set_loopmode(self, mode: Option) { let rmt = crate::peripherals::RMT::regs(); - let ch_idx = self.ch_idx as usize; + let ch = self.ch_idx as usize; if let Some(mode) = mode { - rmt.ch_tx_lim(ch_idx).modify(|_, w| unsafe { - w.loop_count_reset().set_bit(); - w.tx_loop_cnt_en() - .bit(!matches!(mode, super::LoopMode::Infinite)); - w.tx_loop_num().bits(mode.get_count()) - }); + rmt.ch_tx_lim(ch).modify( + #[inline(always)] + |_, w| unsafe { + w.loop_count_reset().set_bit(); + w.tx_loop_cnt_en() + .bit(!matches!(mode, super::LoopMode::Infinite)); + w.tx_loop_num().bits(mode.get_count()) + }, + ); // FIXME: Is this required? - rmt.ch_tx_lim(ch_idx) - .modify(|_, w| w.loop_count_reset().clear_bit()); + rmt.ch_tx_lim(ch).modify( + #[inline(always)] + |_, w| w.loop_count_reset().clear_bit(), + ); } else { - rmt.ch_tx_lim(ch_idx) - .modify(|_, w| w.tx_loop_cnt_en().clear_bit()); + rmt.ch_tx_lim(ch).modify( + #[inline(always)] + |_, w| w.tx_loop_cnt_en().clear_bit(), + ); } } - #[inline] - pub fn clear_tx_interrupts(&self) { + #[inline(always)] + pub fn clear_tx_interrupts(self, events: impl Into>) { let rmt = crate::peripherals::RMT::regs(); - let ch = self.ch_idx as u8; + let events = events.into(); + + rmt.int_clr().write( + #[inline(always)] + |w| { + let ch = self.ch_idx as u8; - rmt.int_clr().write(|w| { - w.ch_err(ch).set_bit(); - w.ch_tx_end(ch).set_bit(); - #[cfg(rmt_has_tx_loop_count)] - w.ch_tx_loop(ch).set_bit(); - w.ch_tx_thr_event(ch).set_bit() - }); + w.ch_tx_end(ch).bit(events.contains(Event::End)); + w.ch_err(ch).bit(events.contains(Event::Error)); + #[cfg(rmt_has_tx_loop_count)] + w.ch_tx_loop(ch).bit(events.contains(Event::LoopCount)); + w.ch_tx_thr_event(ch).bit(events.contains(Event::Threshold)); + + w + }, + ); } - #[inline] - pub fn set_tx_continuous(&self, continuous: bool) { + #[inline(always)] + pub fn set_tx_continuous(self, continuous: bool) { let rmt = crate::peripherals::RMT::regs(); + let ch = self.ch_idx as usize; - rmt.chconf1(self.ch_idx as usize) - .modify(|_, w| w.tx_conti_mode().bit(continuous)); + rmt.chconf1(ch).modify( + #[inline(always)] + |_, w| w.tx_conti_mode().bit(continuous), + ); } - #[inline] - pub fn set_tx_wrap_mode(&self, wrap: bool) { + #[inline(always)] + 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)); + rmt.apb_conf().modify( + #[inline(always)] + |_, w| w.mem_tx_wrap_en().bit(wrap), + ); } - pub fn set_tx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level) { + #[inline(always)] + pub fn set_tx_carrier(self, carrier: bool, high: u16, low: u16, level: Level) { let rmt = crate::peripherals::RMT::regs(); let ch = self.ch_idx as usize; - rmt.chcarrier_duty(ch) - .write(|w| unsafe { w.carrier_high().bits(high).carrier_low().bits(low) }); + rmt.chcarrier_duty(ch).write( + #[inline(always)] + |w| unsafe { w.carrier_high().bits(high).carrier_low().bits(low) }, + ); - rmt.chconf0(ch).modify(|_, w| { - w.carrier_en() - .bit(carrier) - .carrier_out_lv() - .bit(level.into()) - }); + rmt.chconf0(ch).modify( + #[inline(always)] + |_, w| { + w.carrier_en() + .bit(carrier) + .carrier_out_lv() + .bit(level.into()) + }, + ); } - pub fn set_tx_idle_output(&self, enable: bool, level: Level) { + #[inline(always)] + pub fn set_tx_idle_output(self, enable: bool, level: Level) { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf1(self.ch_idx as usize) - .modify(|_, w| w.idle_out_en().bit(enable).idle_out_lv().bit(level.into())); + let ch = self.ch_idx as usize; + + rmt.chconf1(ch).modify( + #[inline(always)] + |_, w| w.idle_out_en().bit(enable).idle_out_lv().bit(level.into()), + ); } - #[inline] - pub fn start_tx(&self) { + #[inline(always)] + pub fn start_tx(self) { let rmt = crate::peripherals::RMT::regs(); let ch = self.ch_idx as u8; for i in 1..self.memsize().blocks() { - rmt.chconf1((ch + i).into()) - .modify(|_, w| w.mem_owner().clear_bit()); + rmt.chconf1((ch + i).into()).modify( + #[inline(always)] + |_, w| w.mem_owner().clear_bit(), + ); } - rmt.chconf1(ch as usize).modify(|_, w| { - w.mem_owner().clear_bit(); - w.mem_rd_rst().set_bit(); - w.apb_mem_rst().set_bit(); - w.tx_start().set_bit() - }); + rmt.chconf1(ch as usize).modify( + #[inline(always)] + |_, w| { + w.mem_owner().clear_bit(); + w.mem_rd_rst().set_bit(); + w.apb_mem_rst().set_bit(); + w.tx_start().set_bit() + }, + ); } // Return the first flag that is set of, in order of decreasing priority, // Event::Error, Event::End, Event::LoopCount, Event::Threshold - #[inline] - pub fn get_tx_status(&self) -> Option { + #[inline(always)] + pub fn get_tx_status(self) -> Option { let rmt = crate::peripherals::RMT::regs(); let reg = rmt.int_raw().read(); let ch = self.ch_idx as u8; @@ -2893,23 +3072,20 @@ mod chip_specific { None } - #[inline] - pub fn reset_tx_threshold_set(&self) { + #[inline(always)] + pub fn set_tx_threshold(self, threshold: u8) { let rmt = crate::peripherals::RMT::regs(); - rmt.int_clr() - .write(|w| w.ch_tx_thr_event(self.ch_idx as u8).set_bit()); - } + let ch = self.ch_idx as usize; - #[inline] - pub fn set_tx_threshold(&self, threshold: u8) { - let rmt = crate::peripherals::RMT::regs(); - rmt.ch_tx_lim(self.ch_idx as usize) - .modify(|_, w| unsafe { w.tx_lim().bits(threshold as u16) }); + rmt.ch_tx_lim(ch).modify( + #[inline(always)] + |_, w| unsafe { w.tx_lim().bits(threshold as u16) }, + ); } #[cfg(rmt_has_tx_loop_count)] - #[inline] - pub fn is_tx_loopcount_interrupt_set(&self) -> bool { + #[inline(always)] + pub fn is_tx_loopcount_interrupt_set(self) -> bool { let rmt = crate::peripherals::RMT::regs(); let ch = self.ch_idx as u8; @@ -2920,8 +3096,8 @@ mod chip_specific { // make the implementation of ContinuousTxTransaction::stop_impl much more awkward. #[cfg(not(rmt_has_tx_loop_count))] #[allow(unused)] - #[inline] - pub fn is_tx_loopcount_interrupt_set(&self) -> bool { + #[inline(always)] + pub fn is_tx_loopcount_interrupt_set(self) -> bool { false } @@ -2929,17 +3105,21 @@ mod chip_specific { // Due to inlining, the compiler should be able to eliminate code in the caller that // depends on this. #[cfg(rmt_has_tx_immediate_stop)] - #[inline] - pub fn stop_tx(&self) -> bool { + #[inline(always)] + pub fn stop_tx(self) -> bool { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf1(self.ch_idx as usize) - .modify(|_, w| w.tx_stop().set_bit()); + let ch = self.ch_idx as usize; + + rmt.chconf1(ch).modify( + #[inline(always)] + |_, w| w.tx_stop().set_bit(), + ); true } #[cfg(not(rmt_has_tx_immediate_stop))] - #[inline] - pub fn stop_tx(&self) -> bool { + #[inline(always)] + pub fn stop_tx(self) -> bool { let ptr = self.channel_ram_start(); for idx in 0..self.memsize().codes() { unsafe { @@ -2949,31 +3129,36 @@ mod chip_specific { false } - #[inline] - pub fn set_tx_interrupt(&self, events: EnumSet, enable: bool) { + #[inline(always)] + pub fn set_tx_interrupt(self, events: EnumSet, enable: bool) { let rmt = crate::peripherals::RMT::regs(); - let ch = self.ch_idx as u8; - rmt.int_ena().modify(|_, w| { - if events.contains(Event::Error) { - w.ch_err(ch).bit(enable); - } - if events.contains(Event::End) { - w.ch_tx_end(ch).bit(enable); - } - if events.contains(Event::Threshold) { - w.ch_tx_thr_event(ch).bit(enable); - } - w - }); + + rmt.int_ena().modify( + #[inline(always)] + |_, w| { + let ch = self.ch_idx as u8; + + if events.contains(Event::Error) { + w.ch_err(ch).bit(enable); + } + if events.contains(Event::End) { + w.ch_tx_end(ch).bit(enable); + } + if events.contains(Event::Threshold) { + w.ch_tx_thr_event(ch).bit(enable); + } + w + }, + ); } #[allow(unused)] - #[inline] - pub fn hw_offset(&self) -> usize { + #[inline(always)] + pub fn hw_offset(self) -> usize { let rmt = crate::peripherals::RMT::regs(); - let ch_idx = self.ch_idx as usize; + let ch = self.ch_idx as usize; - let offset = rmt.chstatus(ch_idx).read().mem_raddr_ex().bits(); + let offset = rmt.chstatus(ch).read().mem_raddr_ex().bits(); usize::from(offset) - self.channel_ram_start_offset() } } @@ -2984,54 +3169,72 @@ mod chip_specific { max_from_register_spec!(u16, chconf0, CHCONF0_SPEC, IDLE_THRES_W); impl DynChannelAccess { - #[inline] - pub fn clear_rx_interrupts(&self) { + #[inline(always)] + pub fn clear_rx_interrupts(self, events: impl Into>) { let rmt = crate::peripherals::RMT::regs(); - let ch = self.ch_idx as u8; + let events = events.into(); + + rmt.int_clr().write( + #[inline(always)] + |w| { + let ch = self.ch_idx as u8; - rmt.int_clr().write(|w| { - w.ch_rx_end(ch).set_bit(); - w.ch_err(ch).set_bit() - }); + w.ch_rx_end(ch).bit(events.contains(Event::End)); + w.ch_err(ch).bit(events.contains(Event::Error)); + + w + }, + ); } - pub fn set_rx_carrier(&self, carrier: bool, high: u16, low: u16, level: Level) { + #[inline(always)] + pub fn set_rx_carrier(self, carrier: bool, high: u16, low: u16, level: Level) { let rmt = crate::peripherals::RMT::regs(); let ch = self.ch_idx as usize; - rmt.chcarrier_duty(ch) - .write(|w| unsafe { w.carrier_high().bits(high).carrier_low().bits(low) }); + rmt.chcarrier_duty(ch).write( + #[inline(always)] + |w| unsafe { w.carrier_high().bits(high).carrier_low().bits(low) }, + ); - rmt.chconf0(ch).modify(|_, w| { - w.carrier_en() - .bit(carrier) - .carrier_out_lv() - .bit(level.into()) - }); + rmt.chconf0(ch).modify( + #[inline(always)] + |_, w| { + w.carrier_en() + .bit(carrier) + .carrier_out_lv() + .bit(level.into()) + }, + ); } - #[inline] - pub fn start_rx(&self) { + #[inline(always)] + pub fn start_rx(self) { let rmt = crate::peripherals::RMT::regs(); let ch = self.ch_idx as u8; for i in 1..self.memsize().blocks() { - rmt.chconf1((ch + i).into()) - .modify(|_, w| w.mem_owner().set_bit()); + rmt.chconf1((ch + i).into()).modify( + #[inline(always)] + |_, w| w.mem_owner().set_bit(), + ); } - rmt.chconf1(ch as usize).modify(|_, w| { - w.mem_owner().set_bit(); - w.mem_wr_rst().set_bit(); - w.apb_mem_rst().set_bit(); - w.rx_en().set_bit() - }); + rmt.chconf1(ch as usize).modify( + #[inline(always)] + |_, w| { + w.mem_owner().set_bit(); + w.mem_wr_rst().set_bit(); + w.apb_mem_rst().set_bit(); + w.rx_en().set_bit() + }, + ); } // Return the first flag that is set of, in order of decreasing priority, // Event::Error, Event::End, Event::Threshold - #[inline] - pub fn get_rx_status(&self) -> Option { + #[inline(always)] + pub fn get_rx_status(self) -> Option { let rmt = crate::peripherals::RMT::regs(); let reg = rmt.int_raw().read(); let ch = self.ch_idx as u8; @@ -3045,9 +3248,10 @@ mod chip_specific { } } - #[inline] - pub fn stop_rx(&self, force: bool) { + #[inline(always)] + pub fn stop_rx(self, force: bool) { let rmt = crate::peripherals::RMT::regs(); + let ch = self.ch_idx as usize; // There's no direct hardware support on these chips for stopping the receiver once it // started: It will only stop when it runs into an error or when it detects that the @@ -3073,87 +3277,115 @@ mod chip_specific { // ensure that an `Error` condition occurs and in any case, we never block here for // long. if force { - if !rmt.chconf1(self.ch_idx as usize).read().rx_en().bit() { + if !rmt.chconf1(ch).read().rx_en().bit() { // Don't lock up trying to stop rx when we didn't start in the first place return; } - let (old_idle_thres, old_div) = - rmt.chconf0(self.ch_idx as usize).from_modify(|r, w| { + let (old_idle_thres, old_div) = rmt.chconf0(ch).from_modify( + #[inline(always)] + |r, w| { let old = (r.idle_thres().bits(), r.div_cnt().bits()); unsafe { w.idle_thres().bits(1); w.div_cnt().bits(1); } old - }); + }, + ); - let (old_filter_en, old_filter_thres) = - rmt.chconf1(self.ch_idx as usize).from_modify(|r, w| { + let (old_filter_en, old_filter_thres) = rmt.chconf1(ch).from_modify( + #[inline(always)] + |r, w| { let old = (r.rx_filter_en().bit(), r.rx_filter_thres().bits()); w.rx_en().clear_bit(); w.rx_filter_en().bit(true); unsafe { w.rx_filter_thres().bits(0xFF) }; w.mem_owner().clear_bit(); old - }); + }, + ); while !matches!(self.get_rx_status(), Some(Event::Error | Event::End)) {} // Restore settings - rmt.chconf0(self.ch_idx as usize).modify(|_, w| unsafe { - w.idle_thres().bits(old_idle_thres); - w.div_cnt().bits(old_div) - }); - - rmt.chconf1(self.ch_idx as usize).modify(|_, w| { - w.rx_filter_en().bit(old_filter_en); - unsafe { w.rx_filter_thres().bits(old_filter_thres) }; - w.mem_owner().set_bit() - }); + rmt.chconf0(ch).modify( + #[inline(always)] + |_, w| unsafe { + w.idle_thres().bits(old_idle_thres); + w.div_cnt().bits(old_div) + }, + ); + + rmt.chconf1(ch).modify( + #[inline(always)] + |_, w| { + w.rx_filter_en().bit(old_filter_en); + unsafe { w.rx_filter_thres().bits(old_filter_thres) }; + w.mem_owner().set_bit() + }, + ); } else { // Only disable, don't abort. - rmt.chconf1(self.ch_idx as usize) - .modify(|_, w| w.rx_en().clear_bit()); + rmt.chconf1(ch).modify( + #[inline(always)] + |_, w| w.rx_en().clear_bit(), + ); } } - pub fn set_rx_filter_threshold(&self, value: u8) { + #[inline(always)] + pub fn set_rx_filter_threshold(self, value: u8) { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf1(self.ch_idx as usize).modify(|_, w| unsafe { - w.rx_filter_en().bit(value > 0); - w.rx_filter_thres().bits(value) - }); + let ch = self.ch_idx as usize; + + rmt.chconf1(ch).modify( + #[inline(always)] + |_, w| unsafe { + w.rx_filter_en().bit(value > 0); + w.rx_filter_thres().bits(value) + }, + ); } - pub fn set_rx_idle_threshold(&self, value: u16) { + #[inline(always)] + pub fn set_rx_idle_threshold(self, value: u16) { let rmt = crate::peripherals::RMT::regs(); - rmt.chconf0(self.ch_idx as usize) - .modify(|_, w| unsafe { w.idle_thres().bits(value) }); + let ch = self.ch_idx as usize; + + rmt.chconf0(ch).modify( + #[inline(always)] + |_, w| unsafe { w.idle_thres().bits(value) }, + ); } - #[inline] - pub fn set_rx_interrupt(&self, events: EnumSet, enable: bool) { + #[inline(always)] + pub fn set_rx_interrupt(self, events: EnumSet, enable: bool) { let rmt = crate::peripherals::RMT::regs(); - let ch = self.ch_idx as u8; - rmt.int_ena().modify(|_, w| { - if events.contains(Event::Error) { - w.ch_err(ch).bit(enable); - } - if events.contains(Event::End) { - w.ch_rx_end(ch).bit(enable); - } - w - }); + + rmt.int_ena().modify( + #[inline(always)] + |_, w| { + let ch = self.ch_idx as u8; + + if events.contains(Event::Error) { + w.ch_err(ch).bit(enable); + } + if events.contains(Event::End) { + w.ch_rx_end(ch).bit(enable); + } + w + }, + ); } - #[inline] - pub fn hw_offset(&self) -> usize { + #[inline(always)] + pub fn hw_offset(self) -> usize { let rmt = crate::peripherals::RMT::regs(); - let ch_idx = self.ch_idx as usize; + let ch = self.ch_idx as usize; - let offset = rmt.chstatus(ch_idx).read().mem_waddr_ex().bits(); + let offset = rmt.chstatus(ch).read().mem_waddr_ex().bits(); usize::from(offset) - self.channel_ram_start_offset() } } diff --git a/esp-hal/src/rmt/reader.rs b/esp-hal/src/rmt/reader.rs index edcd5e0c73..5599ab6718 100644 --- a/esp-hal/src/rmt/reader.rs +++ b/esp-hal/src/rmt/reader.rs @@ -12,6 +12,7 @@ pub(crate) enum ReaderState { Done, } +#[derive(Debug)] pub(crate) struct RmtReader { // The position in channel RAM to continue reading from; must be either // 0 or half the available RAM size if there's further data. diff --git a/esp-hal/src/rmt/writer.rs b/esp-hal/src/rmt/writer.rs index f7b6be7faa..ada31261ef 100644 --- a/esp-hal/src/rmt/writer.rs +++ b/esp-hal/src/rmt/writer.rs @@ -3,7 +3,7 @@ use procmacros::ram; use super::{DynChannelAccess, Error, PulseCode, Tx}; -#[derive(PartialEq)] +#[derive(Debug, PartialEq)] pub(crate) enum WriterState { Active, @@ -12,6 +12,7 @@ pub(crate) enum WriterState { Done, } +#[derive(Debug)] pub(crate) struct RmtWriter { // The position in channel RAM to continue writing at; must be either // 0 or half the available RAM size if there's further data. diff --git a/examples/async/embassy_rmt_rx/src/main.rs b/examples/async/embassy_rmt_rx/src/main.rs index c4b7d19199..0ce1bce9b4 100644 --- a/examples/async/embassy_rmt_rx/src/main.rs +++ b/examples/async/embassy_rmt_rx/src/main.rs @@ -68,11 +68,11 @@ async fn main(spawner: Spawner) { cfg_if::cfg_if! { if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { - let mut channel = rmt.channel0.configure_rx(peripherals.GPIO4, rx_config).unwrap(); + let mut channel = rmt.channel0.configure_rx(peripherals.GPIO4, &rx_config).unwrap(); } else if #[cfg(feature = "esp32s3")] { - let mut channel = rmt.channel7.configure_rx(peripherals.GPIO4, rx_config).unwrap(); + let mut channel = rmt.channel7.configure_rx(peripherals.GPIO4, &rx_config).unwrap(); } else { - let mut channel = rmt.channel2.configure_rx(peripherals.GPIO4, rx_config).unwrap(); + let mut channel = rmt.channel2.configure_rx(peripherals.GPIO4, &rx_config).unwrap(); } } diff --git a/examples/async/embassy_rmt_tx/src/main.rs b/examples/async/embassy_rmt_tx/src/main.rs index 9b345b135d..c7a2827635 100644 --- a/examples/async/embassy_rmt_tx/src/main.rs +++ b/examples/async/embassy_rmt_tx/src/main.rs @@ -53,7 +53,7 @@ async fn main(_spawner: Spawner) { .channel0 .configure_tx( peripherals.GPIO4, - TxChannelConfig::default().with_clk_divider(255), + &TxChannelConfig::default().with_clk_divider(255), ) .unwrap(); diff --git a/hil-test/src/bin/rmt.rs b/hil-test/src/bin/rmt.rs index 740c6519c3..9052e586c4 100644 --- a/hil-test/src/bin/rmt.rs +++ b/hil-test/src/bin/rmt.rs @@ -248,7 +248,7 @@ fn do_rmt_loopback_inner( Err((err, _channel)) => Err(err), } } - Err(e) => Err(e), + Err((e, _)) => Err(e), }; let rx_res = run(); @@ -264,7 +264,7 @@ fn do_rmt_loopback(ctx: &mut Context, tx_memsize: u8, rx_me .with_idle_threshold(1000) .with_memsize(rx_memsize); - let (tx_channel, rx_channel) = ctx.setup_loopback(tx_config, rx_config); + let (tx_channel, rx_channel) = ctx.setup_loopback(&tx_config, &rx_config); do_rmt_loopback_inner::(tx_channel, rx_channel, false, rx_memsize); } @@ -317,7 +317,7 @@ async fn do_rmt_loopback_async( .with_idle_threshold(1000) .with_memsize(rx_memsize); - let (mut tx_channel, mut rx_channel) = ctx.setup_loopback_async(tx_config, rx_config); + let (mut tx_channel, mut rx_channel) = ctx.setup_loopback_async(&tx_config, &rx_config); do_rmt_loopback_async_inner::(&mut tx_channel, &mut rx_channel, false, rx_memsize).await } @@ -333,11 +333,15 @@ fn do_rmt_single_shot( .with_clk_divider(DIV) .with_memsize(tx_memsize); - let (tx_channel, _) = ctx.setup_loopback(tx_config, Default::default()); + let (tx_channel, _) = ctx.setup_loopback(&tx_config, &Default::default()); let tx_data: [_; TX_LEN] = generate_tx_data(write_end_marker, write_end_marker); - tx_channel.transmit(&tx_data)?.wait().map_err(|(e, _)| e)?; + tx_channel + .transmit(&tx_data) + .map_err(|(e, _)| e)? + .wait() + .map_err(|(e, _)| e)?; Ok(()) } @@ -374,16 +378,18 @@ impl Context { rmt: Rmt<'a, Dm>, rx: impl PeripheralInput<'a>, tx: impl PeripheralOutput<'a>, - tx_config: TxChannelConfig, - rx_config: RxChannelConfig, + tx_config: &TxChannelConfig, + rx_config: &RxChannelConfig, ) -> (Channel<'a, Dm, Tx>, Channel<'a, Dm, Rx>) { let tx_channel = rmt .channel0 - .configure_tx(tx, tx_config.with_clk_divider(DIV)) + .configure_tx(tx, &tx_config.with_clk_divider(DIV)) + .map_err(|(e, _, _)| e) .unwrap(); let rx_channel = rx_channel_creator!(rmt) - .configure_rx(rx, rx_config.with_clk_divider(DIV)) + .configure_rx(rx, &rx_config.with_clk_divider(DIV)) + .map_err(|(e, _, _)| e) .unwrap(); (tx_channel, rx_channel) @@ -391,8 +397,8 @@ impl Context { fn setup_loopback( &mut self, - tx_config: TxChannelConfig, - rx_config: RxChannelConfig, + tx_config: &TxChannelConfig, + rx_config: &RxChannelConfig, ) -> (Channel<'_, Blocking, Tx>, Channel<'_, Blocking, Rx>) { let rmt = Rmt::new(self.rmt.reborrow(), FREQ).unwrap(); let (rx, tx) = pins!(self); @@ -401,8 +407,8 @@ impl Context { fn setup_loopback_async( &mut self, - tx_config: TxChannelConfig, - rx_config: RxChannelConfig, + tx_config: &TxChannelConfig, + rx_config: &RxChannelConfig, ) -> (Channel<'_, Async, Tx>, Channel<'_, Async, Rx>) { let rmt = Rmt::new(self.rmt.reborrow(), FREQ).unwrap().into_async(); let (rx, tx) = pins!(self); @@ -513,13 +519,16 @@ mod tests { let _ch0 = rmt .channel0 - .configure_tx(NoPin, TxChannelConfig::default().with_memsize(2)) + .configure_tx(NoPin, &TxChannelConfig::default().with_memsize(2)) + .map_err(|(e, _, _)| e) .unwrap(); // Configuring channel 1 should fail, since channel 0 already uses its memory. - let ch1 = rmt.channel1.configure_tx(NoPin, TxChannelConfig::default()); + let ch1 = rmt + .channel1 + .configure_tx(NoPin, &TxChannelConfig::default()); - assert!(matches!(ch1, Err(Error::MemoryBlockNotAvailable))); + assert!(matches!(ch1, Err((Error::MemoryBlockNotAvailable, _, _)))); } #[test] @@ -528,14 +537,16 @@ mod tests { let ch0 = rmt .channel0 - .configure_tx(NoPin, TxChannelConfig::default().with_memsize(2)) + .configure_tx(NoPin, &TxChannelConfig::default().with_memsize(2)) + .map_err(|(e, _, _)| e) .unwrap(); // After dropping channel 0, the memory that it reserved should become available // again such that channel 1 configuration succeeds. core::mem::drop(ch0); rmt.channel1 - .configure_tx(NoPin, TxChannelConfig::default()) + .configure_tx(NoPin, &TxChannelConfig::default()) + .map_err(|(e, _, _)| e) .unwrap(); } @@ -554,13 +565,15 @@ mod tests { let tx_channel = rmt .$tx_channel .reborrow() - .configure_tx(tx_pin, tx_config) + .configure_tx(tx_pin, &tx_config) + .map_err(|(e, _, _)| e) .unwrap(); let rx_channel = rmt .$rx_channel .reborrow() - .configure_rx(rx_pin, rx_config) + .configure_rx(rx_pin, &rx_config) + .map_err(|(e, _, _)| e) .unwrap(); do_rmt_loopback_inner::<20>(tx_channel, rx_channel, false, 1); @@ -642,7 +655,7 @@ mod tests { // idle threshold 16383 cycles = ~33ms let rx_config = RxChannelConfig::default().with_idle_threshold(0x3FFF); - let (_, rx_channel) = Context::setup_impl(rmt, rx_pin, NoPin, tx_config, rx_config); + let (_, rx_channel) = Context::setup_impl(rmt, rx_pin, NoPin, &tx_config, &rx_config); let mut rx_data = [PulseCode::default(); 6]; let rx_transaction = rx_channel.receive(&mut rx_data).unwrap(); @@ -685,7 +698,8 @@ mod tests { let mut ch0 = rmt .channel0 .reborrow() - .configure_tx(NoPin, TxChannelConfig::default()) + .configure_tx(NoPin, &TxChannelConfig::default()) + .map_err(|(e, _, _)| e) .unwrap(); let tx_data: [_; 10] = generate_tx_data(true, true); @@ -706,7 +720,7 @@ mod tests { .with_idle_output(true); let rx_config = RxChannelConfig::default().with_idle_threshold(1000); - let (mut tx_channel, mut rx_channel) = ctx.setup_loopback(tx_config, rx_config); + let (mut tx_channel, mut rx_channel) = ctx.setup_loopback(&tx_config, &rx_config); do_rmt_loopback_inner::(tx_channel.reborrow(), rx_channel.reborrow(), true, 1); @@ -734,12 +748,14 @@ mod tests { let mut tx_channel = rmt .channel0 .reborrow() - .configure_tx(tx, tx_config.with_clk_divider(DIV)) + .configure_tx(tx, &tx_config.with_clk_divider(DIV)) + .map_err(|(e, _, _)| e) .unwrap(); let mut rx_channel = rx_channel_creator!(rmt) .reborrow() - .configure_rx(rx, rx_config.with_clk_divider(DIV)) + .configure_rx(rx, &rx_config.with_clk_divider(DIV)) + .map_err(|(e, _, _)| e) .unwrap(); // Test that dropping & recreating Channel works @@ -767,7 +783,7 @@ mod tests { .with_idle_output(true); let rx_config = RxChannelConfig::default().with_idle_threshold(1000); - let (mut tx_channel, mut rx_channel) = ctx.setup_loopback_async(tx_config, rx_config); + let (mut tx_channel, mut rx_channel) = ctx.setup_loopback_async(&tx_config, &rx_config); // Start loopback once, but abort before completion... do_rmt_loopback_async_inner::(&mut tx_channel, &mut rx_channel, true, 1).await; @@ -799,12 +815,14 @@ mod tests { let mut tx_channel = rmt .channel0 .reborrow() - .configure_tx(tx, tx_config.with_clk_divider(DIV)) + .configure_tx(tx, &tx_config.with_clk_divider(DIV)) + .map_err(|(e, _, _)| e) .unwrap(); let mut rx_channel = rx_channel_creator!(rmt) .reborrow() - .configure_rx(rx, rx_config.with_clk_divider(DIV)) + .configure_rx(rx, &rx_config.with_clk_divider(DIV)) + .map_err(|(e, _, _)| e) .unwrap(); // Test that dropping & recreating Channel works @@ -831,7 +849,7 @@ mod tests { .with_idle_threshold(1000) .with_memsize(2); - let (mut tx_channel, mut rx_channel) = ctx.setup_loopback(tx_config, rx_config); + let (mut tx_channel, mut rx_channel) = ctx.setup_loopback(&tx_config, &rx_config); let tx_data: [_; TX_COUNT + 1] = generate_tx_data(false, true); let mut rx_data: [PulseCode; MAX_RX_LEN] = [Default::default(); MAX_RX_LEN]; @@ -916,7 +934,7 @@ mod tests { .with_idle_output(true) .with_idle_output_level(Level::Low); - let (mut tx_channel, _) = ctx.setup_loopback(tx_config, RxChannelConfig::default()); + let (mut tx_channel, _) = ctx.setup_loopback(&tx_config, &RxChannelConfig::default()); let tx_data: [_; 10] = generate_tx_data(false, true);