Skip to main content

esp_idf_hal/rmt/
config.rs

1use core::time::Duration;
2
3use esp_idf_sys::{EspError, TickType_t, ESP_ERR_INVALID_ARG};
4
5use crate::rmt::ClockSource;
6use crate::units::{FromValueType, Hertz};
7
8/// A percentage from 0 to 100%, used to specify the duty percentage in [`CarrierConfig`].
9#[derive(Debug, Copy, Clone, PartialEq)]
10pub struct DutyPercent(f32);
11
12impl DutyPercent {
13    /// Must be between 0 and 1.0, otherwise an error is returned.
14    pub fn new(value: f32) -> Result<Self, EspError> {
15        if !(0.0..=1.0).contains(&value) {
16            Err(EspError::from_infallible::<ESP_ERR_INVALID_ARG>())
17        } else {
18            Ok(Self(value))
19        }
20    }
21
22    /// Returns the percentage as a value between 0.0 and 1.0.
23    #[must_use]
24    pub const fn to_f32(&self) -> f32 {
25        self.0
26    }
27}
28
29/// Configuration setting for looping a signal.
30#[derive(Copy, Clone, Debug, PartialEq, Eq)]
31#[non_exhaustive]
32pub enum Loop {
33    /// No looping, transmit the signal only once.
34    None,
35    /// Repeats the transmission indefinitely.
36    Endless,
37    /// Repeats the transmission for the specified number of times.
38    Count(u32),
39}
40
41/// Controls how the channel accesses memory.
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub enum MemoryAccess {
44    /// Enables the DMA backend for the channel. Using the DMA allows a significant amount of
45    /// the channel's workload to be offloaded from the CPU. However, the DMA backend is not
46    /// available on all ESP chips, please refer to [TRM] before you enable this option.
47    /// Or you might encounter a `ESP_ERR_NOT_SUPPORTED` error.
48    ///
49    /// [TRM]: https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#rmt
50    Direct {
51        /// This field controls the size of the internal DMA buffer. To achieve a better
52        /// throughput and smaller CPU overhead, you can set a larger value, e.g., `1024`.
53        memory_block_symbols: usize,
54    },
55    /// Disables direct memory access. The CPU will have to read/write the data transferred from/to the RMT memory.
56    Indirect {
57        /// This field controls the size of the dedicated memory block owned by the channel,
58        /// which should be at least 64.
59        memory_block_symbols: usize,
60    },
61}
62
63impl MemoryAccess {
64    #[must_use]
65    pub(crate) fn is_direct(&self) -> bool {
66        matches!(self, Self::Direct { .. })
67    }
68
69    #[must_use]
70    pub(crate) fn symbols(&self) -> usize {
71        let (Self::Direct {
72            memory_block_symbols,
73        }
74        | Self::Indirect {
75            memory_block_symbols,
76        }) = self;
77        *memory_block_symbols
78    }
79}
80
81impl Default for MemoryAccess {
82    fn default() -> Self {
83        Self::Indirect {
84            memory_block_symbols: 64,
85        }
86    }
87}
88
89#[derive(Debug, Clone)]
90pub struct TxChannelConfig {
91    /// Selects the source clock for the RMT channel.
92    ///
93    /// Note that, the selected clock is also used by other channels,
94    /// which means the user should ensure this configuration is the
95    /// same when allocating other channels, regardless of TX or RX.
96    /// For the effect on the power consumption of different clock sources,
97    /// please refer to the [Power Management] section.
98    ///
99    /// [Power Management]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/rmt.html#rmt-power-management
100    pub clock_source: ClockSource,
101    /// Sets the resolution of the internal tick counter. The timing parameter
102    /// of the RMT signal is calculated based on this **tick**.
103    pub resolution: Hertz,
104    /// Controls how the channel accesses memory.
105    pub memory_access: MemoryAccess,
106    /// Sets the depth of the internal transaction queue, the deeper the queue,
107    /// the more transactions can be prepared in the backlog.
108    pub transaction_queue_depth: usize,
109    /// Set the priority of the interrupt. If set to 0, then the driver will use a
110    /// interrupt with low or medium priority (priority level may be one of 1, 2 or 3),
111    /// otherwise use the priority indicated by [`TxChannelConfig::interrupt_priority`].
112    ///
113    /// Please pay attention that once the interrupt priority is set, it cannot be changed
114    /// until the channel is dropped.
115    #[cfg(esp_idf_version_at_least_5_1_2)]
116    pub interrupt_priority: i32,
117    /// Is used to decide whether to invert the RMT signal before sending it to the GPIO pad.
118    pub invert_out: bool,
119    /// The signal output from the GPIO will be fed to the input path as well.
120    pub io_loop_back: bool,
121    /// Configure the GPIO as open-drain mode.
122    pub io_od_mode: bool,
123    /// Configures if the driver allows the system to power down the peripheral in light sleep mode.
124    /// Before entering sleep, the system will backup the RMT register context, which will be restored
125    /// later when the system exit the sleep mode. Powering down the peripheral can save more power,
126    /// but at the cost of more memory consumed to save the register context. It's a tradeoff between
127    /// power consumption and memory consumption. This configuration option relies on specific
128    /// hardware feature, if you enable it on an unsupported chip, you will see error message
129    /// like not able to power down in light sleep.
130    #[cfg(esp_idf_version_at_least_5_4_0)]
131    pub allow_pd: bool,
132    // This field is intentionally hidden to prevent non-exhaustive pattern matching.
133    // You should only construct this struct using the `..Default::default()` pattern.
134    // If you use this field directly, your code might break in future versions.
135    #[doc(hidden)]
136    #[allow(dead_code)]
137    pub __internal: (),
138}
139
140impl Default for TxChannelConfig {
141    fn default() -> Self {
142        Self {
143            clock_source: Default::default(),
144            resolution: 1.MHz().into(),
145            memory_access: Default::default(),
146            transaction_queue_depth: 4,
147            #[cfg(esp_idf_version_at_least_5_1_2)]
148            interrupt_priority: 0,
149            invert_out: false,
150            io_loop_back: false,
151            io_od_mode: false,
152            #[cfg(esp_idf_version_at_least_5_4_0)]
153            allow_pd: false,
154            __internal: (),
155        }
156    }
157}
158
159#[derive(Debug, Clone)]
160pub struct TransmitConfig {
161    /// Specify the times of transmission in a loop, -1 means transmitting in an infinite loop
162    pub loop_count: Loop,
163    /// Set the output level for the "End Of Transmission"
164    pub eot_level: bool,
165    /// If set, when the transaction queue is full, driver will not block the thread but return directly.
166    #[cfg(esp_idf_version_at_least_5_1_3)]
167    pub queue_non_blocking: bool,
168    // This field is intentionally hidden to prevent non-exhaustive pattern matching.
169    // You should only construct this struct using the `..Default::default()` pattern.
170    // If you use this field directly, your code might break in future versions.
171    #[doc(hidden)]
172    #[allow(dead_code)]
173    pub __internal: (),
174}
175
176impl Default for TransmitConfig {
177    fn default() -> Self {
178        Self {
179            loop_count: Loop::None,
180            eot_level: false,
181            #[cfg(esp_idf_version_at_least_5_1_3)]
182            queue_non_blocking: false,
183            __internal: (),
184        }
185    }
186}
187
188#[derive(Debug, Clone)]
189pub struct CarrierConfig {
190    /// Carrier wave frequency, in Hz, 0 means disabling the carrier.
191    pub frequency: Hertz,
192    /// Carrier wave duty cycle (0~100%)
193    pub duty_cycle: DutyPercent,
194    /// Specify the polarity of carrier, by default it's modulated to base signal's high level
195    pub polarity_active_low: bool,
196    /// If set, the carrier can always exist even there's not transfer undergoing
197    pub always_on: bool,
198    // This field is intentionally hidden to prevent non-exhaustive pattern matching.
199    // You should only construct this struct using the `..Default::default()` pattern.
200    // If you use this field directly, your code might break in future versions.
201    #[doc(hidden)]
202    #[allow(dead_code)]
203    pub __internal: (),
204}
205
206impl Default for CarrierConfig {
207    fn default() -> Self {
208        Self {
209            frequency: 38000.Hz(),
210            duty_cycle: DutyPercent(0.5),
211            polarity_active_low: false,
212            always_on: false,
213            __internal: (),
214        }
215    }
216}
217
218#[derive(Debug, Clone)]
219pub struct RxChannelConfig {
220    /// Selects the source clock for the RMT channel.
221    ///
222    /// Note that, the selected clock is also used by other channels,
223    /// which means the user should ensure this configuration is the
224    /// same when allocating other channels, regardless of TX or RX.
225    /// For the effect on the power consumption of different clock sources,
226    /// please refer to the [Power Management] section.
227    ///
228    /// [Power Management]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/rmt.html#rmt-power-management
229    pub clock_source: ClockSource,
230    /// Sets the resolution of the internal tick counter. The timing parameter
231    /// of the RMT signal is calculated based on this **tick**.
232    pub resolution: Hertz,
233    /// Controls how the channel accesses memory.
234    pub memory_access: MemoryAccess,
235    /// Set the priority of the interrupt. If set to 0, then the driver will use a
236    /// interrupt with low or medium priority (priority level may be one of 1, 2 or 3),
237    /// otherwise use the priority indicated by [`RxChannelConfig::interrupt_priority`].
238    ///
239    /// Please pay attention that once the interrupt priority is set, it cannot be changed
240    /// until the channel is dropped.
241    #[cfg(esp_idf_version_at_least_5_1_2)]
242    pub interrupt_priority: i32,
243    /// Is used to decide whether to invert the incoming RMT signal.
244    pub invert_in: bool,
245    /// The signal output from the GPIO will be fed to the input path as well.
246    pub io_loop_back: bool,
247    /// Configures if the driver allows the system to power down the peripheral in light sleep mode.
248    /// Before entering sleep, the system will backup the RMT register context, which will be restored
249    /// later when the system exit the sleep mode. Powering down the peripheral can save more power,
250    /// but at the cost of more memory consumed to save the register context. It's a tradeoff between
251    /// power consumption and memory consumption. This configuration option relies on specific
252    /// hardware feature, if you enable it on an unsupported chip, you will see error message
253    /// like not able to power down in light sleep.
254    #[cfg(esp_idf_version_at_least_5_4_0)]
255    pub allow_pd: bool,
256    // This field is intentionally hidden to prevent non-exhaustive pattern matching.
257    // You should only construct this struct using the `..Default::default()` pattern.
258    // If you use this field directly, your code might break in future versions.
259    #[doc(hidden)]
260    #[allow(dead_code)]
261    pub __internal: (),
262}
263
264impl Default for RxChannelConfig {
265    fn default() -> Self {
266        Self {
267            clock_source: Default::default(),
268            resolution: 1.MHz().into(),
269            memory_access: Default::default(),
270            #[cfg(esp_idf_version_at_least_5_1_2)]
271            interrupt_priority: 0,
272            invert_in: false,
273            io_loop_back: false,
274            #[cfg(esp_idf_version_at_least_5_4_0)]
275            allow_pd: false,
276            __internal: (),
277        }
278    }
279}
280
281#[derive(Debug, Clone)]
282pub struct ReceiveConfig {
283    /// Specifies the minimal valid pulse duration in either high or low logic levels.
284    ///
285    /// A pulse width that is smaller than this value is treated as a glitch, and ignored by the hardware.
286    pub signal_range_min: Duration,
287    /// Specifies the maximum valid pulse duration in either high or low logic levels.
288    ///
289    /// A pulse width that is bigger than this value is treated as Stop Signal, and the
290    /// receiver generates receive-complete event immediately.
291    pub signal_range_max: Duration,
292    /// Set this flag if the incoming data is very long, and the driver can only receive the data
293    /// piece by piece, because the user buffer is not sufficient to save all the data.
294    #[cfg(esp_idf_version_at_least_5_3_0)]
295    pub enable_partial_rx: bool,
296    /// Maximum time to wait for data to be received.
297    ///
298    /// If `None`, the driver will wait indefinitely.
299    pub timeout: Option<TickType_t>,
300    // This field is intentionally hidden to prevent non-exhaustive pattern matching.
301    // You should only construct this struct using the `..Default::default()` pattern.
302    // If you use this field directly, your code might break in future versions.
303    #[doc(hidden)]
304    #[allow(dead_code)]
305    pub __internal: (),
306}
307
308impl Default for ReceiveConfig {
309    fn default() -> Self {
310        Self {
311            signal_range_min: Duration::ZERO,
312            // This is the maximum duration that the driver supports.
313            signal_range_max: Duration::from_nanos(u32::MAX as u64),
314            #[cfg(esp_idf_version_at_least_5_3_0)]
315            enable_partial_rx: false,
316            timeout: None,
317            __internal: (),
318        }
319    }
320}