Skip to main content

esp_idf_hal/
rmt.rs

1//! RMT (Remote Control) peripheral support.
2//!
3//! The RMT (Remote Control Transceiver) peripheral was designed to act as an infrared transceiver.
4//! However, due to the flexibility of its data format, RMT can be extended to a versatile and
5//! general-purpose transceiver, transmitting or receiving many other types of signals.
6//! From the perspective of network layering, the RMT hardware contains both physical and data
7//! link layers. The physical layer defines the communication media and bit signal representation.
8//! The data link layer defines the format of an RMT frame. The minimal data unit in the frame is
9//! called the RMT symbol, which is represented by [`Symbol`] in the driver.
10//!
11//! ESP32 contains multiple channels in the RMT peripheral. Each channel can be independently
12//! configured as either transmitter or receiver.
13//!
14//! Typically, the RMT peripheral can be used in the following scenarios:
15//! - Transmit or receive infrared signals, with any IR protocols, e.g., NEC
16//! - General-purpose sequence generator
17//! - Transmit signals in a hardware-controlled loop, with a finite or infinite number of times
18//! - Multi-channel simultaneous transmission
19//! - Modulate the carrier to the output signal or demodulate the carrier from the input signal
20//!
21//! # Driver redesign in ESP-IDF 5.0
22//!
23//! In ESP-IDF 5.0, the [RMT API was redesigned] to simplify and unify the usage of the
24//! RMT peripheral.
25//!
26//! It is recommended to use the new API, but for now the old API is available through
27//! the `rmt-legacy` feature. The ESP-IDF 6.0 release will remove support for the legacy API.
28//!
29//! [RMT API was redesigned]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/migration-guides/release-5.x/5.0/peripherals.html#rmt-driver
30pub mod config;
31pub mod encoder;
32
33mod tx_channel;
34pub use tx_channel::*;
35
36mod tx_queue;
37pub use tx_queue::*;
38
39mod rx_channel;
40pub use rx_channel::*;
41
42mod sync_manager;
43pub use sync_manager::*;
44mod pulse;
45pub use pulse::*;
46
47use core::time::Duration;
48use core::{fmt, ptr};
49
50use esp_idf_sys::*;
51
52use crate::rmt::config::CarrierConfig;
53use crate::units::Hertz;
54
55#[cfg(esp_idf_version_at_least_6_0_0)]
56#[allow(non_camel_case_types)]
57type rmt_carrier_config_t__bindgen_ty_1 = rmt_carrier_config_t_extra_rmt_carrier_config_flags;
58
59/// A [`Symbol`] constists of two [`Pulse`]s, where each pulse defines a level of the pin (high or low)
60/// and a duration ([`PulseTicks`]).
61///
62/// The `Pulse`s can be the same level (e.g., both high) and must not necessarily be different levels.
63///
64/// This is just a newtype over the IDF's `rmt_item32_t` or `rmt_symbol_word_t` type.
65#[derive(Clone, Copy)]
66#[repr(transparent)]
67pub struct Symbol(rmt_symbol_word_t);
68
69impl Symbol {
70    /// Create a symbol from a pair of half-cycles.
71    #[must_use]
72    pub fn new(level0: Pulse, level1: Pulse) -> Self {
73        let item = rmt_symbol_word_t { val: 0 };
74        let mut this = Self(item);
75        this.update(level0, level1);
76        this
77    }
78
79    /// Constructs a symbol from the given levels and durations.
80    ///
81    /// This is a convenience function that combines [`Pulse::new_with_duration`] and [`Symbol::new`].
82    pub fn new_with(
83        resolution: Hertz,
84        level0: PinState,
85        duration0: Duration,
86        level1: PinState,
87        duration1: Duration,
88    ) -> Result<Self, EspError> {
89        Ok(Self::new(
90            Pulse::new_with_duration(resolution, level0, duration0)?,
91            Pulse::new_with_duration(resolution, level1, duration1)?,
92        ))
93    }
94
95    /// Constructs a new symbol where the duration is split evenly between the two levels.
96    ///
97    /// It is guaranteed that the combined duration of the two levels adds up to the given duration.
98    /// If the duration is odd, the first level might be a bit shorter than the second level.
99    pub fn new_half_split(
100        resolution: Hertz,
101        level0: PinState,
102        level1: PinState,
103        duration: Duration,
104    ) -> Result<Self, EspError> {
105        let first_half_duration = duration / 2;
106        // This ensures that the two halves always add up to the original duration,
107        // even if the duration is odd.
108        let second_half_duration = duration - first_half_duration;
109
110        Ok(Self::new(
111            Pulse::new_with_duration(resolution, level0, first_half_duration)?,
112            Pulse::new_with_duration(resolution, level1, second_half_duration)?,
113        ))
114    }
115
116    /// Repeats the symbol to have the returned sequence of symbols last for
117    /// exactly the given duration.
118    ///
119    /// There is an upper limit for how long a single symbol can be ([`PulseTicks::max`]).
120    /// To create a symbol that lasts longer than that, it is split into multiple symbols.
121    /// The returned iterator yields as many symbols as necessary to reach the
122    /// desired duration.
123    ///
124    /// If the given duration is not a multiple of the symbol duration,
125    /// the last symbol will be adjusted to occupy the remaining time.
126    ///
127    /// # Panics
128    ///
129    /// If `self` has a duration of zero.
130    pub fn repeat_for(&self, resolution: Hertz, duration: Duration) -> impl Iterator<Item = Self> {
131        // Calculate the maximum allowed duration for a single symbol consisting of two pulses:
132        // let max_duration = PulseTicks::max().duration(resolution) * 2;
133
134        let symbol_duration = self.duration(resolution);
135
136        // Handle edge-case to prevent an infinite loop:
137        if symbol_duration.is_zero() {
138            panic!("Cannot repeat a symbol with zero duration for {duration:?}");
139        }
140
141        let count = duration.as_nanos() / symbol_duration.as_nanos();
142        let remainder =
143            Duration::from_nanos((duration.as_nanos() % symbol_duration.as_nanos()) as u64);
144
145        let last_symbol = {
146            if remainder.is_zero() {
147                None
148            } else {
149                let duration0 = self.level0().ticks.duration(resolution).min(remainder);
150                let duration1 = remainder.saturating_sub(duration0);
151
152                Some(
153                    Self::new_with(
154                        resolution,
155                        self.level0().pin_state,
156                        duration0,
157                        self.level1().pin_state,
158                        duration1,
159                    )
160                    .unwrap(),
161                )
162            }
163        };
164
165        core::iter::repeat_n(*self, count as usize).chain(core::iter::once(last_symbol).flatten())
166    }
167
168    #[must_use]
169    fn symbol_word_to_pulse(word: &rmt_symbol_word_t) -> (Pulse, Pulse) {
170        let inner = unsafe { &word.__bindgen_anon_1 };
171        (
172            Pulse::new(
173                (inner.level0() as u32).into(),
174                PulseTicks::new(inner.duration0()).unwrap(),
175            ),
176            Pulse::new(
177                (inner.level1() as u32).into(),
178                PulseTicks::new(inner.duration1()).unwrap(),
179            ),
180        )
181    }
182
183    /// Returns the first half-cycle (pulse) of this symbol.
184    #[must_use]
185    pub fn level0(&self) -> Pulse {
186        Self::symbol_word_to_pulse(&self.0).0
187    }
188
189    /// Returns the second half-cycle (pulse) of this symbol.
190    #[must_use]
191    pub fn level1(&self) -> Pulse {
192        Self::symbol_word_to_pulse(&self.0).1
193    }
194
195    /// Returns the combined duration of both half-cycles of this symbol.
196    pub fn duration(&self, resolution: Hertz) -> Duration {
197        self.level0().ticks.duration(resolution) + self.level1().ticks.duration(resolution)
198    }
199
200    /// Mutate this symbol to store a different pair of half-cycles.
201    pub fn update(&mut self, level0: Pulse, level1: Pulse) {
202        // SAFETY: We're overriding all 32 bits, so it doesn't matter what was here before.
203        let inner = unsafe { &mut self.0.__bindgen_anon_1 };
204        inner.set_level0(level0.pin_state as u16);
205        inner.set_duration0(level0.ticks.ticks());
206        inner.set_level1(level1.pin_state as u16);
207        inner.set_duration1(level1.ticks.ticks());
208    }
209}
210
211impl fmt::Debug for Symbol {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        f.debug_struct("Symbol")
214            .field("level0", &self.level0())
215            .field("level1", &self.level1())
216            .finish()
217    }
218}
219
220impl Default for Symbol {
221    fn default() -> Self {
222        Self::new(Default::default(), Default::default())
223    }
224}
225
226impl PartialEq for Symbol {
227    fn eq(&self, other: &Self) -> bool {
228        (self.level0(), self.level1()) == (other.level0(), other.level1())
229    }
230}
231
232impl Eq for Symbol {}
233
234impl From<rmt_symbol_word_t> for Symbol {
235    fn from(value: rmt_symbol_word_t) -> Self {
236        Self(value)
237    }
238}
239
240impl From<Symbol> for rmt_symbol_word_t {
241    fn from(value: Symbol) -> Self {
242        value.0
243    }
244}
245
246/// A small helper function to assert that the current code is not running in an ISR.
247///
248/// This is useful for functions that must not be called from an ISR context.
249fn assert_not_in_isr() {
250    if crate::interrupt::active() {
251        panic!("This function cannot be called from an ISR");
252    }
253}
254
255/// Type of RMT TX done event data.
256#[derive(Debug, Clone, Copy)]
257#[non_exhaustive]
258pub struct TxDoneEventData {
259    /// The number of symbols ([`Symbol`]) that have been transmitted.
260    ///
261    /// This also reflects the size of the encoding artifacts. Please
262    /// note, this value accounts for the `EOF` symbol as well, which
263    /// is automatically appended by the driver to mark the end of one
264    /// transaction.
265    pub num_symbols: usize,
266}
267
268impl From<rmt_tx_done_event_data_t> for TxDoneEventData {
269    fn from(data: rmt_tx_done_event_data_t) -> Self {
270        Self {
271            num_symbols: data.num_symbols,
272        }
273    }
274}
275
276/// Type of RMT RX done event data.
277#[derive(Debug, Clone, Copy)]
278#[non_exhaustive]
279pub struct RxDoneEventData {
280    /// Pointer to the received RMT symbols.
281    ///
282    /// These symbols are saved in the internal `buffer` of the `RxChannelDriver`.
283    /// You are not allowed to free this buffer.
284    ///
285    /// If the partial receive feature is enabled, then the buffer will be
286    /// used as a "second level buffer", where its content can be overwritten by
287    /// data coming in afterwards. In this case, you should copy the received data to
288    /// another place if you want to keep it or process it later.
289    pub received_symbols: *mut Symbol,
290    /// The number of received RMT symbols.
291    ///
292    /// This value will never be larger than the buffer size of the buffer passed to the
293    /// receive function.
294    ///
295    /// If the buffer is not sufficient to accomodate all the received RMT symbols, the
296    /// driver only keeps the maximum number of symbols that the buffer can hold, and excess
297    /// symbols are discarded or ignored.
298    pub num_symbols: usize,
299    /// Indicates whether the current received data is the last part of the transaction.
300    ///
301    /// This is useful for when [`ReceiveConfig::enable_partial_rx`] is enabled,
302    /// and the data is received in parts.
303    ///
304    /// For receives where partial rx is not enabled, this field will always be `true`.
305    ///
306    /// [`ReceiveConfig::enable_partial_rx`]: crate::rmt::config::ReceiveConfig::enable_partial_rx
307    #[cfg(esp_idf_version_at_least_5_3_0)]
308    pub is_last: bool,
309}
310
311impl RxDoneEventData {
312    /// Returns the received symbols as a slice.
313    ///
314    /// # Safety
315    ///
316    /// Both the pointer and the length must be valid.
317    /// If these haven't been changed from the values provided by the driver,
318    /// this should be the case **in** the ISR callback.
319    pub unsafe fn as_slice(&self) -> &[Symbol] {
320        core::slice::from_raw_parts(self.received_symbols, self.num_symbols)
321    }
322}
323
324impl From<rmt_rx_done_event_data_t> for RxDoneEventData {
325    fn from(data: rmt_rx_done_event_data_t) -> Self {
326        Self {
327            received_symbols: data.received_symbols as *mut Symbol,
328            num_symbols: data.num_symbols,
329            #[cfg(esp_idf_version_at_least_5_3_0)]
330            is_last: data.flags.is_last() != 0,
331        }
332    }
333}
334
335/// A trait implemented by an RMT channel that provides common functionality.
336pub trait RmtChannel {
337    /// Returns the underlying `rmt_channel_handle_t`.
338    fn handle(&self) -> rmt_channel_handle_t;
339
340    /// Returns whether the channel is currently enabled.
341    #[must_use]
342    fn is_enabled(&self) -> bool;
343
344    /// Sets the internal `is_enabled` flag of the channel that is used to track the channel state.
345    ///
346    /// # Safety
347    ///
348    /// The channel assumes that the given `is_enabled` reflects the state of the channel.
349    /// There shouldn't be any reason for the user to call this function directly.
350    /// Use `enable`/`disable` instead.
351    #[doc(hidden)]
352    unsafe fn set_internal_enabled(&mut self, is_enabled: bool);
353
354    /// Must be called in advance before transmitting or receiving RMT symbols.
355    /// For TX channels, enabling a channel enables a specific interrupt and
356    /// prepares the hardware to dispatch transactions.
357    ///
358    /// For RX channels, enabling a channel enables an interrupt, but the receiver
359    /// is not started during this time, as the characteristics of the incoming
360    /// signal have yet to be specified.
361    ///
362    /// The receiver is started in [`RxChannel::receive`].
363    fn enable(&mut self) -> Result<(), EspError> {
364        esp!(unsafe { rmt_enable(self.handle()) })?;
365
366        // SAFETY: The channel has been enabled -> it is safe to mark it as enabled.
367        unsafe { self.set_internal_enabled(true) };
368        Ok(())
369    }
370
371    /// Disables the interrupt and clearing any pending interrupts. The transmitter
372    /// and receiver are disabled as well.
373    ///
374    /// # Note
375    ///
376    /// This function will release a power management (PM) lock that might be
377    /// installed during channel allocation
378    ///
379    /// # Errors
380    ///
381    /// - `ESP_ERR_INVALID_ARG`: Disable RMT channel failed because of invalid argument
382    /// - `ESP_ERR_INVALID_STATE`: Disable RMT channel failed because it's not enabled yet
383    /// - `ESP_FAIL`: Disable RMT channel failed because of other error
384    fn disable(&mut self) -> Result<(), EspError> {
385        esp!(unsafe { rmt_disable(self.handle()) })?;
386        // SAFETY: The channel has been disable -> it is safe to mark it as disabled.
387        unsafe { self.set_internal_enabled(false) };
388        Ok(())
389    }
390
391    /// Apply (de)modulation feature for the channel.
392    ///
393    /// If `carrier_config` is `None`, the carrier (de)modulation will be disabled.
394    ///
395    /// # Errors
396    ///
397    /// - `ESP_ERR_INVALID_ARG`: Apply carrier failed because of invalid argument
398    /// - `ESP_FAIL`: Apply carrier failed because of other error
399    fn apply_carrier(&mut self, carrier_config: Option<&CarrierConfig>) -> Result<(), EspError> {
400        let mut sys_carrier = None;
401        if let Some(CarrierConfig {
402            frequency,
403            duty_cycle,
404            polarity_active_low,
405            always_on,
406            // match __internal to get a compiler error if new fields are added in the future
407            __internal,
408        }) = carrier_config
409        {
410            sys_carrier = Some(rmt_carrier_config_t {
411                frequency_hz: (*frequency).into(),
412                duty_cycle: duty_cycle.to_f32(),
413                flags: rmt_carrier_config_t__bindgen_ty_1 {
414                    _bitfield_1: rmt_carrier_config_t__bindgen_ty_1::new_bitfield_1(
415                        *polarity_active_low as u32,
416                        *always_on as u32,
417                    ),
418                    ..Default::default()
419                },
420            })
421        }
422
423        esp!(unsafe {
424            rmt_apply_carrier(
425                self.handle(),
426                sys_carrier.as_ref().map_or(ptr::null(), |c| c as *const _),
427            )
428        })
429    }
430}
431
432impl<R: RmtChannel> RmtChannel for &mut R {
433    fn handle(&self) -> rmt_channel_handle_t {
434        (**self).handle()
435    }
436
437    fn is_enabled(&self) -> bool {
438        (**self).is_enabled()
439    }
440
441    unsafe fn set_internal_enabled(&mut self, is_enabled: bool) {
442        (**self).set_internal_enabled(is_enabled)
443    }
444
445    fn enable(&mut self) -> Result<(), EspError> {
446        (**self).enable()
447    }
448
449    fn disable(&mut self) -> Result<(), EspError> {
450        (**self).disable()
451    }
452
453    fn apply_carrier(&mut self, carrier_config: Option<&CarrierConfig>) -> Result<(), EspError> {
454        (**self).apply_carrier(carrier_config)
455    }
456}
457
458/// Clock source for RMT channels.
459#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
460pub enum ClockSource {
461    #[default]
462    Default,
463    #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))]
464    APB,
465    #[cfg(any(esp32c3, esp32c5, esp32c6, esp32h2, esp32h4, esp32p4, esp32s3))]
466    RcFast,
467    #[cfg(any(esp32, esp32s2))]
468    RefTick,
469    #[cfg(any(esp32c3, esp32c5, esp32c6, esp32h2, esp32h4, esp32p4, esp32s3))]
470    XTAL,
471    #[cfg(any(esp32c5, esp32c6, esp32p4))]
472    PLLF80M,
473}
474
475impl From<ClockSource> for rmt_clock_source_t {
476    fn from(clock: ClockSource) -> Self {
477        match clock {
478            ClockSource::Default => soc_periph_rmt_clk_src_t_RMT_CLK_SRC_DEFAULT,
479            #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))]
480            ClockSource::APB => soc_periph_rmt_clk_src_t_RMT_CLK_SRC_APB,
481            #[cfg(any(esp32c3, esp32c5, esp32c6, esp32h2, esp32h4, esp32p4, esp32s3))]
482            ClockSource::RcFast => soc_periph_rmt_clk_src_t_RMT_CLK_SRC_RC_FAST,
483            #[cfg(any(esp32, esp32s2))]
484            ClockSource::RefTick => soc_periph_rmt_clk_src_t_RMT_CLK_SRC_REF_TICK,
485            #[cfg(any(esp32c3, esp32c5, esp32c6, esp32h2, esp32h4, esp32p4, esp32s3))]
486            ClockSource::XTAL => soc_periph_rmt_clk_src_t_RMT_CLK_SRC_XTAL,
487            #[cfg(any(esp32c5, esp32c6, esp32p4))]
488            ClockSource::PLLF80M => soc_periph_rmt_clk_src_t_RMT_CLK_SRC_PLL_F80M,
489        }
490    }
491}