1
#![cfg_attr(docsrs, procmacros::doc_replace(
2
    "freq" => {
3
        cfg(esp32h2) => "32",
4
        _ => "80"
5
    },
6
    "channel" => {
7
        cfg(any(esp32, esp32s2)) => "channel0",
8
        cfg(any(esp32s3, esp32s31, esp32p4)) => "channel7",
9
        _ => "channel2"
10
    },
11
    "channels_desc" => {
12
        cfg(esp32) => "8 channels, each of them can be either receiver or transmitter",
13
        cfg(esp32s2) => "4 channels, each of them can be either receiver or transmitter",
14
        cfg(any(esp32s3, esp32s31, esp32p4)) => "8 channels, `Channel<0>`-`Channel<3>` hardcoded for transmitting signals and `Channel<4>`-`Channel<7>` hardcoded for receiving signals",
15
        cfg(any(esp32c3, esp32c5, esp32c6, esp32h2)) => "4 channels, `Channel<0>` and `Channel<1>` hardcoded for transmitting signals and `Channel<2>` and `Channel<3>` hardcoded for receiving signals",
16
    }
17
))]
18
//! # Remote Control Peripheral (RMT)
19
//!
20
//! ## Overview
21
//! The RMT (Remote Control) module is designed to send and receive infrared
22
//! remote control signals. A variety of remote control protocols can be
23
//! encoded/decoded via software based on the RMT module. The RMT module
24
//! converts pulse codes stored in the module’s built-in RAM into output
25
//! signals, or converts input signals into pulse codes and stores them in RAM.
26
//! In addition, the RMT module optionally modulates its output signals with a
27
//! carrier wave, or optionally demodulates and filters its input signals.
28
//!
29
//! Typically, the RMT peripheral can be used in the following scenarios:
30
//! - Transmit or receive infrared signals, with any IR protocols, e.g., NEC
31
//! - General-purpose sequence generator
32
//! - Transmit signals in a hardware-controlled loop, with a finite or infinite number of times
33
//! - Modulate the carrier to the output signal or demodulate the carrier from the input signal
34
//!
35
//! ### Channels
36
//!
37
//! There are __channels_desc__.
38
//!
39
//! For more information, please refer to the
40
#![doc = concat!("[ESP-IDF documentation](https://docs.espressif.com/projects/esp-idf/en/latest/", chip!(), "/api-reference/peripherals/rmt.html)")]
41
//! ## Configuration
42
//! Each TX/RX channel has the same functionality controlled by a dedicated set
43
//! of registers and is able to independently transmit or receive data. TX
44
//! channels are indicated by n which is used as a placeholder for the channel
45
//! number, and by m for RX channels.
46
//!
47
//! ## Examples
48
//!
49
//! ### Initialization
50
//!
51
//! ```rust, no_run
52
//! # {before_snippet}
53
//! # use esp_hal::gpio::Level;
54
//! # use esp_hal::peripherals::Peripherals;
55
//! # use esp_hal::rmt::TxChannelConfig;
56
//! # use esp_hal::rmt::Rmt;
57
//! # use crate::esp_hal::rmt::TxChannelCreator;
58
//! let rmt = Rmt::new(peripherals.RMT, Rate::from_mhz(__freq__))?;
59
//! let mut channel = rmt
60
//!     .channel0
61
//!     .configure_tx(
62
//!         &TxChannelConfig::default()
63
//!             .with_clk_divider(1)
64
//!             .with_idle_output_level(Level::Low)
65
//!             .with_idle_output(false)
66
//!             .with_carrier_modulation(false)
67
//!             .with_carrier_high(1)
68
//!             .with_carrier_low(1)
69
//!             .with_carrier_level(Level::Low),
70
//!     )?
71
//!     .with_pin(peripherals.GPIO1);
72
//! # {after_snippet}
73
//! ```
74
//!
75
//! ### TX operation
76
//! ```rust, no_run
77
//! # {before_snippet}
78
//! # use esp_hal::delay::Delay;
79
//! # use esp_hal::gpio::Level;
80
//! # use esp_hal::rmt::{PulseCode, Rmt, TxChannelConfig, TxChannelCreator};
81
//! #
82
//! // Configure frequency based on chip type
83
//! let rmt = Rmt::new(peripherals.RMT, Rate::from_mhz(__freq__))?;
84
//!
85
//! let tx_config = TxChannelConfig::default().with_clk_divider(255);
86
//!
87
//! let mut channel = rmt
88
//!     .channel0
89
//!     .configure_tx(&tx_config)?
90
//!     .with_pin(peripherals.GPIO4);
91
//!
92
//! let delay = Delay::new();
93
//!
94
//! let mut data = [PulseCode::new(Level::High, 200, Level::Low, 50); 20];
95
//! data[data.len() - 2] = PulseCode::new(Level::High, 3000, Level::Low, 500);
96
//! data[data.len() - 1] = PulseCode::end_marker();
97
//!
98
//! loop {
99
//!     let transaction = channel.transmit(&data)?;
100
//!     channel = transaction.wait()?;
101
//!     delay.delay_millis(500);
102
//! }
103
//! # }
104
//! ```
105
//!
106
//! ### RX operation
107
//! ```rust, no_run
108
//! # {before_snippet}
109
//! # use esp_hal::rmt::{PulseCode, Rmt, RxChannelConfig, RxChannelCreator};
110
//! # use esp_hal::delay::Delay;
111
//! # use esp_hal::gpio::{Level, Output, OutputConfig};
112
//! #
113
//! const WIDTH: usize = 80;
114
//!
115
//! let mut out = Output::new(peripherals.GPIO5, Level::Low, OutputConfig::default());
116
//!
117
//! // Configure frequency based on chip type
118
//! let rmt = Rmt::new(peripherals.RMT, Rate::from_mhz(__freq__))?;
119
//!
120
//! let rx_config = RxChannelConfig::default()
121
//!     .with_clk_divider(1)
122
//!     .with_idle_threshold(10000);
123
//! let mut channel = rmt
124
//!     .__channel__
125
//!     .configure_rx(&rx_config)?
126
//!     .with_pin(peripherals.GPIO4);
127
//! let delay = Delay::new();
128
//! let mut data: [PulseCode; 48] = [PulseCode::default(); 48];
129
//!
130
//! loop {
131
//!     for x in data.iter_mut() {
132
//!         x.reset()
133
//!     }
134
//!
135
//!     let transaction = channel.receive(&mut data)?;
136
//!
137
//!     // Simulate input
138
//!     for i in 0u32..5u32 {
139
//!         out.set_high();
140
//!         delay.delay_micros(i * 10 + 20);
141
//!         out.set_low();
142
//!         delay.delay_micros(i * 20 + 20);
143
//!     }
144
//!
145
//!     match transaction.wait() {
146
//!         Ok((symbol_count, channel_res)) => {
147
//!             channel = channel_res;
148
//!             let mut total = 0usize;
149
//!             for entry in &data[..symbol_count] {
150
//!                 if entry.length1() == 0 {
151
//!                     break;
152
//!                 }
153
//!                 total += entry.length1() as usize;
154
//!
155
//!                 if entry.length2() == 0 {
156
//!                     break;
157
//!                 }
158
//!                 total += entry.length2() as usize;
159
//!             }
160
//!
161
//!             for entry in &data[..symbol_count] {
162
//!                 if entry.length1() == 0 {
163
//!                     break;
164
//!                 }
165
//!
166
//!                 let count = WIDTH / (total / entry.length1() as usize);
167
//!                 let c = match entry.level1() {
168
//!                     Level::High => '-',
169
//!                     Level::Low => '_',
170
//!                 };
171
//!                 for _ in 0..count + 1 {
172
//!                     print!("{}", c);
173
//!                 }
174
//!
175
//!                 if entry.length2() == 0 {
176
//!                     break;
177
//!                 }
178
//!
179
//!                 let count = WIDTH / (total / entry.length2() as usize);
180
//!                 let c = match entry.level2() {
181
//!                     Level::High => '-',
182
//!                     Level::Low => '_',
183
//!                 };
184
//!                 for _ in 0..count + 1 {
185
//!                     print!("{}", c);
186
//!                 }
187
//!             }
188
//!
189
//!             println!();
190
//!         }
191
//!         Err((_err, channel_res)) => {
192
//!             channel = channel_res;
193
//!         }
194
//!     }
195
//!
196
//!     delay.delay_millis(1500);
197
//! }
198
//! # }
199
//! ```
200
//!
201
//! > Note: on ESP32 and ESP32-S2 you cannot specify a base frequency other than 80 MHz
202

            
203
use core::{
204
    default::Default,
205
    marker::PhantomData,
206
    pin::Pin,
207
    task::{Context, Poll},
208
};
209

            
210
use enumset::{EnumSet, EnumSetType};
211
use esp_sync::RawMutex;
212
use portable_atomic::Ordering;
213
#[cfg(place_rmt_driver_in_ram)]
214
use procmacros::ram;
215

            
216
use crate::{
217
    Async,
218
    Blocking,
219
    asynch::AtomicWaker,
220
    gpio::{
221
        self,
222
        InputConfig,
223
        Level,
224
        OutputConfig,
225
        PinGuard,
226
        interconnect::{PeripheralInput, PeripheralOutput},
227
    },
228
    peripherals::{Interrupt, RMT},
229
    soc::clocks,
230
    system::GenericPeripheralGuard,
231
    time::Rate,
232
};
233

            
234
mod reader;
235
use reader::{ReaderState, RmtReader};
236
mod writer;
237
use writer::{RmtWriter, WriterState};
238

            
239
/// A configuration error.
240
#[derive(Debug, Clone, Copy, PartialEq)]
241
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
242
#[non_exhaustive]
243
pub enum ConfigError {
244
    /// The desired frequency is impossible to reach.
245
    UnreachableTargetFrequency,
246
    /// The idle threshold exceeds [`MAX_RX_IDLE_THRESHOLD`].
247
    IdleThresholdOutOfRange,
248
    /// The memsize is 0 or larger than what the channel can support.
249
    MemsizeOutOfRange,
250
    /// (Part of) the requested channel memory is in use by another channel.
251
    MemoryBlockNotAvailable,
252
}
253

            
254
impl core::error::Error for ConfigError {}
255

            
256
impl core::fmt::Display for ConfigError {
257
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
258
        match self {
259
            Self::UnreachableTargetFrequency => {
260
                write!(f, "The desired frequency is impossible to reach")
261
            }
262
            Self::IdleThresholdOutOfRange => write!(f, "The idle threshold is out of range"),
263
            Self::MemsizeOutOfRange => write!(f, "The memsize is out of range"),
264
            Self::MemoryBlockNotAvailable => {
265
                write!(f, "Memory block is not available for channel")
266
            }
267
        }
268
    }
269
}
270

            
271
/// Errors
272
#[derive(Debug, Clone, Copy, PartialEq)]
273
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
274
#[allow(clippy::enum_variant_names, reason = "peripheral is unstable")]
275
#[non_exhaustive]
276
pub enum Error {
277
    /// The amount of pulses exceeds the size of the FIFO.
278
    Overflow,
279
    /// An argument is invalid.
280
    InvalidArgument,
281
    /// An error occurred during transmission.
282
    TransmissionError,
283
    /// No transmission end marker found.
284
    EndMarkerMissing,
285
    /// The data length is invalid.
286
    InvalidDataLength,
287
    /// Receiver error most likely RMT memory overflow.
288
    ReceiverError,
289
}
290

            
291
impl core::error::Error for Error {}
292

            
293
impl core::fmt::Display for Error {
294
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
295
        match self {
296
            Self::Overflow => write!(f, "The amount of pulses exceeds the size of the FIFO"),
297
            Self::InvalidArgument => write!(f, "An argument is invalid"),
298
            Self::TransmissionError => write!(f, "An error occurred during transmission"),
299
            Self::EndMarkerMissing => write!(f, "No transmission end marker found"),
300
            Self::InvalidDataLength => write!(f, "The data length is invalid"),
301
            Self::ReceiverError => write!(f, "Receiver error most likely RMT memory overflow"),
302
        }
303
    }
304
}
305

            
306
/// Convenience newtype to work with pulse codes.
307
///
308
/// A [`PulseCode`] is represented as `u32`, with fields laid out as follows:
309
///
310
/// | Bit 31   | Bits 30-16 | Bit 15   | Bits 14-0 |
311
/// |----------|------------|----------|-----------|
312
/// | `level2` | `length2`  | `level1` | `length1` |
313
///
314
/// Here, `level1` / `length1` correspond to the signal that is send/received first,
315
/// and the signal with `level2` / `length2` is send/received afterwards.
316
///
317
/// If `length1` or `length2` are zero, this implies an end marker and transmission will
318
/// stop with the corresponding signal.
319
#[derive(Clone, Copy, Default, Eq, PartialEq)]
320
#[repr(transparent)]
321
pub struct PulseCode(pub u32);
322

            
323
// Pre-compute some constants to make it obvious that the code below doesn't mix up both halves of
324
// a PulseCode.
325
const LENGTH1_SHIFT: usize = 0;
326
const LEVEL1_SHIFT: usize = 15;
327
const LENGTH2_SHIFT: usize = 16;
328
const LEVEL2_SHIFT: usize = 31;
329

            
330
const LENGTH_MASK: u32 = 0x7FFF;
331
const LENGTH1_MASK: u32 = LENGTH_MASK << LENGTH1_SHIFT;
332
const LENGTH2_MASK: u32 = LENGTH_MASK << LENGTH2_SHIFT;
333

            
334
const LEVEL1_MASK: u32 = 1 << LEVEL1_SHIFT;
335
const LEVEL2_MASK: u32 = 1 << LEVEL2_SHIFT;
336

            
337
impl PulseCode {
338
    /// Maximum value for the `length1` and `length2` fields.
339
    pub const MAX_LEN: u16 = 0x7FFF;
340

            
341
    /// Creates a new instance.
342
    ///
343
    /// Panics if `length1` or `length2` exceed the maximum representable range.
344
    #[inline]
345
    pub const fn new(level1: Level, length1: u16, level2: Level, length2: u16) -> Self {
346
        if length1 > Self::MAX_LEN || length2 > Self::MAX_LEN {
347
            // defmt::panic! fails const eval
348
            core::panic!("PulseCode length out of range");
349
        };
350

            
351
        // SAFETY:
352
        // - We just checked that length1 and length2 are in range
353
        unsafe { Self::new_unchecked(level1, length1, level2, length2) }
354
    }
355

            
356
    /// Creates a new instance.
357
    ///
358
    /// If `length1` or `length2` exceed the maximum representable range, they
359
    /// will be clamped to `Self::MAX_LEN`.
360
    #[inline]
361
    pub const fn new_clamped(level1: Level, length1: u16, level2: Level, length2: u16) -> Self {
362
        // Can't use lengthX.min(Self::MAX_LEN) since it is not const
363
        let length1 = if length1 > Self::MAX_LEN {
364
            Self::MAX_LEN
365
        } else {
366
            length1
367
        };
368
        let length2 = if length2 > Self::MAX_LEN {
369
            Self::MAX_LEN
370
        } else {
371
            length2
372
        };
373

            
374
        // SAFETY:
375
        // - We just clamped length1 and length2 to the required intervals
376
        unsafe { Self::new_unchecked(level1, length1, level2, length2) }
377
    }
378

            
379
    /// Creates a new instance, attempting to convert lengths to `u16` first.
380
    ///
381
    /// This is slightly more convenient when passing in longer integers (e.g. `u32`) resulting from
382
    /// a preceding calculation.
383
    ///
384
    /// If `length1` or `length2` fail to convert to `u16` or exceed the maximum representable
385
    /// range, this will return `None`.
386
    #[inline]
387
    pub fn try_new(
388
        level1: Level,
389
        length1: impl TryInto<u16>,
390
        level2: Level,
391
        length2: impl TryInto<u16>,
392
    ) -> Option<Self> {
393
        let (Ok(length1), Ok(length2)) = (length1.try_into(), length2.try_into()) else {
394
            return None;
395
        };
396
        if length1 > Self::MAX_LEN || length2 > Self::MAX_LEN {
397
            return None;
398
        }
399

            
400
        // SAFETY:
401
        // - We just checked that length1 and length2 are in range
402
        Some(unsafe { Self::new_unchecked(level1, length1, level2, length2) })
403
    }
404

            
405
    /// Creates a new instance without checking that code lengths are in range.
406
    ///
407
    /// # Safety
408
    ///
409
    /// `length1` and `length2` must be 15-bit wide, i.e. their MSB must be cleared.
410
    #[inline]
411
    pub const unsafe fn new_unchecked(
412
        level1: Level,
413
        length1: u16,
414
        level2: Level,
415
        length2: u16,
416
    ) -> Self {
417
        Self(
418
            (level1.const_into() as u32) << LEVEL1_SHIFT
419
                | (level2.const_into() as u32) << LEVEL2_SHIFT
420
                | (length1 as u32) << LENGTH1_SHIFT
421
                | (length2 as u32) << LENGTH2_SHIFT,
422
        )
423
    }
424

            
425
    /// Creates a new instance that is an end marker with `Level::Low`.
426
    ///
427
    /// This corresponds to the all-zero [`PulseCode`], i.e. with both level and
428
    /// length fields set to zero, equivalent to (but more semantic than)
429
    /// `PulseCode::from(0u32)` and [`PulseCode::default()`].
430
    // FIXME: Consider adding a variant with `level1`, `length1` and `level2` arguments
431
    // which sets `length2 = 0` so that it is still guaranteed to return an end
432
    // marker.
433
    #[inline]
434
    pub const fn end_marker() -> Self {
435
        Self(0)
436
    }
437

            
438
    /// Sets all levels and lengths to 0.
439
    ///
440
    /// In other words, assigns the value of [`PulseCode::end_marker()`] to `self`.
441
    #[inline]
442
    pub fn reset(&mut self) {
443
        self.0 = 0
444
    }
445

            
446
    /// Logical output level in the first pulse code interval
447
    #[inline]
448
    pub const fn level1(self) -> Level {
449
        let level = (self.0 >> LEVEL1_SHIFT) & 1;
450
        Level::const_from(0 != level)
451
    }
452

            
453
    /// Logical output level in the second pulse code interval
454
    #[inline]
455
    pub const fn level2(self) -> Level {
456
        let level = (self.0 >> LEVEL2_SHIFT) & 1;
457
        Level::const_from(0 != level)
458
    }
459

            
460
    /// Length of the first pulse code interval (in clock cycles)
461
    #[inline]
462
    pub const fn length1(self) -> u16 {
463
        ((self.0 >> LENGTH1_SHIFT) & LENGTH_MASK) as u16
464
    }
465

            
466
    /// Length of the second pulse code interval (in clock cycles)
467
    #[inline]
468
    pub const fn length2(self) -> u16 {
469
        ((self.0 >> LENGTH2_SHIFT) & LENGTH_MASK) as u16
470
    }
471

            
472
    /// Sets `level1` and returns the modified [`PulseCode`].
473
    #[inline]
474
    pub const fn with_level1(mut self, level: Level) -> Self {
475
        self.0 &= !LEVEL1_MASK;
476
        self.0 |= (level.const_into() as u32) << LEVEL1_SHIFT;
477
        self
478
    }
479

            
480
    /// Sets `level2` and returns the modified [`PulseCode`].
481
    #[inline]
482
    pub const fn with_level2(mut self, level: Level) -> Self {
483
        self.0 &= !LEVEL2_MASK;
484
        self.0 |= (level.const_into() as u32) << LEVEL2_SHIFT;
485
        self
486
    }
487

            
488
    /// Sets `length1` and returns the modified [`PulseCode`].
489
    ///
490
    /// Returns `None` if `length` exceeds the representable range.
491
    #[inline]
492
    pub const fn with_length1(mut self, length: u16) -> Option<Self> {
493
        if length > Self::MAX_LEN {
494
            return None;
495
        }
496

            
497
        self.0 &= !LENGTH1_MASK;
498
        self.0 |= (length as u32) << LENGTH1_SHIFT;
499
        Some(self)
500
    }
501

            
502
    /// Sets `length2` and returns the modified [`PulseCode`].
503
    ///
504
    /// Returns `None` if `length` exceeds the representable range.
505
    #[inline]
506
    pub const fn with_length2(mut self, length: u16) -> Option<Self> {
507
        if length > Self::MAX_LEN {
508
            return None;
509
        }
510

            
511
        self.0 &= !LENGTH2_MASK;
512
        self.0 |= (length as u32) << LENGTH2_SHIFT;
513
        Some(self)
514
    }
515

            
516
    /// Returns whether this pulse code contains an end marker.
517
    ///
518
    /// Equivalent to `self.length1() == 0 || self.length2() == 0`.
519
    #[inline]
520
    pub const fn is_end_marker(self) -> bool {
521
        self.length1() == 0 || self.length2() == 0
522
    }
523

            
524
    #[inline]
525
    fn symbol1(self) -> char {
526
        if self.level1().into() { 'H' } else { 'L' }
527
    }
528

            
529
    #[inline]
530
    fn symbol2(self) -> char {
531
        if self.level2().into() { 'H' } else { 'L' }
532
    }
533
}
534

            
535
impl core::fmt::Debug for PulseCode {
536
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
537
        write!(
538
            f,
539
            "PulseCode({} {}, {} {})",
540
            self.symbol1(),
541
            self.length1(),
542
            self.symbol2(),
543
            self.length2(),
544
        )
545
    }
546
}
547

            
548
#[cfg(feature = "defmt")]
549
impl defmt::Format for PulseCode {
550
    fn format(&self, fmt: defmt::Formatter<'_>) {
551
        defmt::write!(
552
            fmt,
553
            "PulseCode({} {}, {} {})",
554
            self.symbol1(),
555
            self.length1(),
556
            self.symbol2(),
557
            self.length2(),
558
        )
559
    }
560
}
561

            
562
impl From<u32> for PulseCode {
563
    #[inline]
564
    fn from(value: u32) -> Self {
565
        Self(value)
566
    }
567
}
568

            
569
impl From<PulseCode> for u32 {
570
    #[inline]
571
    fn from(code: PulseCode) -> u32 {
572
        code.0
573
    }
574
}
575

            
576
/// Memory size associated to a channel.
577
///
578
/// This is a newtype around the number of blocks as u8, thus not requiring any
579
/// extra space. However, it is useful to abstract memory sizes into their own
580
/// type to make explicit whether they refer to a number of RAM blocks or a
581
/// number of pulse codes and to centralize conversion between both.
582
#[derive(Copy, Clone)]
583
struct MemSize(u8);
584

            
585
impl MemSize {
586
    /// Creates from the given number of RMT RAM blocks.
587
    #[inline]
588
    const fn from_blocks(blocks: u8) -> Self {
589
        Self(blocks)
590
    }
591

            
592
    /// Returns the number of RMT RAM blocks specified by this `MemSize`.
593
    #[inline]
594
    const fn blocks(self) -> u8 {
595
        self.0
596
    }
597

            
598
    /// Returns the number of RMT pulse codes specified by this `MemSize`.
599
    #[inline]
600
    const fn codes(self) -> usize {
601
        self.0 as usize * property!("rmt.channel_ram_size")
602
    }
603
}
604

            
605
/// Marker for a channel capable of or configured for transmit operations.
606
#[derive(Clone, Copy, Debug)]
607
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
608
pub struct Tx;
609

            
610
/// Marker for a channel capable of or configured for receive operations.
611
#[derive(Clone, Copy, Debug)]
612
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
613
pub struct Rx;
614

            
615
/// A trait implemented by the `Rx` and `Tx` marker structs.
616
///
617
/// For internal use by the driver.
618
pub trait Direction: Copy + Clone + core::fmt::Debug + crate::private::Sealed + Unpin {
619
    #[doc(hidden)]
620
    const IS_TX: bool;
621
}
622

            
623
impl crate::private::Sealed for Tx {}
624

            
625
impl crate::private::Sealed for Rx {}
626

            
627
impl Direction for Tx {
628
    const IS_TX: bool = true;
629
}
630

            
631
impl Direction for Rx {
632
    const IS_TX: bool = false;
633
}
634

            
635
#[derive(Clone, Copy, Debug)]
636
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
637
struct DynChannelAccess<Dir: Direction> {
638
    ch_idx: ChannelIndex,
639
    _direction: PhantomData<Dir>,
640
}
641

            
642
impl<Dir: Direction> DynChannelAccess<Dir> {
643
    #[inline]
644
    unsafe fn conjure(ch_idx: ChannelIndex) -> Self {
645
        Self {
646
            ch_idx,
647
            _direction: PhantomData,
648
        }
649
    }
650
}
651

            
652
/// Channel configuration for TX channels
653
#[derive(Debug, Copy, Clone, procmacros::BuilderLite)]
654
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
655
pub struct TxChannelConfig {
656
    /// Channel's clock divider
657
    clk_divider: u8,
658
    /// Whether the idle output level is low/high.
659
    idle_output_level: Level,
660
    /// Whether idle output is enabled.
661
    idle_output: bool,
662
    /// Whether carrier modulation is enabled.
663
    carrier_modulation: bool,
664
    /// Carrier high phase in ticks
665
    carrier_high: u16,
666
    /// Carrier low phase in ticks
667
    carrier_low: u16,
668
    /// Level of the carrier
669
    carrier_level: Level,
670
    /// The amount of memory blocks allocated to this channel.
671
    memsize: u8,
672
}
673

            
674
impl Default for TxChannelConfig {
675
    fn default() -> Self {
676
        Self {
677
            clk_divider: Default::default(),
678
            idle_output_level: Level::Low,
679
            idle_output: Default::default(),
680
            carrier_modulation: Default::default(),
681
            carrier_high: Default::default(),
682
            carrier_low: Default::default(),
683
            carrier_level: Level::Low,
684
            memsize: 1,
685
        }
686
    }
687
}
688

            
689
/// Channel configuration for RX channels
690
2
#[derive(Debug, Copy, Clone, procmacros::BuilderLite)]
691
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
692
pub struct RxChannelConfig {
693
    /// Channel's clock divider
694
    clk_divider: u8,
695
    /// Whether carrier demodulation is enabled.
696
    carrier_modulation: bool,
697
    /// Carrier high phase in ticks
698
    carrier_high: u16,
699
    /// Carrier low phase in ticks
700
    carrier_low: u16,
701
    /// Level of the carrier
702
    carrier_level: Level,
703
    /// Filter threshold in ticks
704
    filter_threshold: u8,
705
    /// Idle threshold in ticks, must not exceed [`MAX_RX_IDLE_THRESHOLD`].
706
    idle_threshold: u16,
707
    /// The amount of memory blocks allocated to this channel.
708
    memsize: u8,
709
}
710

            
711
impl Default for RxChannelConfig {
712
    fn default() -> Self {
713
        Self {
714
            clk_divider: Default::default(),
715
            carrier_modulation: Default::default(),
716
            carrier_high: Default::default(),
717
            carrier_low: Default::default(),
718
            carrier_level: Level::Low,
719
            filter_threshold: Default::default(),
720
            idle_threshold: Default::default(),
721
            memsize: 1,
722
        }
723
    }
724
}
725

            
726
// Channel specification
727

            
728
// Looping all channels: Build up the Rmt struct and compute NUM_CHANNELS
729
for_each_rmt_channel!(
730
    (all $(($num:literal)),+) => {
731
        paste::paste! {
732
            /// RMT Instance
733
            pub struct Rmt<'rmt, Dm>
734
            where
735
                Dm: crate::DriverMode,
736
            {
737
                peripheral: RMT<'rmt>,
738
                frequency: Rate,
739
                $(
740
                    #[doc = concat!("RMT Channel ", $num)]
741
                    pub [<channel $num>]: ChannelCreator<'rmt, Dm, $num>,
742
                )+
743
                _mode: PhantomData<Dm>,
744
            }
745

            
746
            impl<'rmt, Dm> Rmt<'rmt, Dm>
747
            where
748
                Dm: crate::DriverMode,
749
            {
750
                fn create(peripheral: RMT<'rmt>, frequency: Rate) -> Self {
751
                    Self {
752
                        peripheral,
753
                        frequency,
754
                        $(
755
                            [<channel $num>]: ChannelCreator::conjure(),
756
                        )+
757
                        _mode: PhantomData,
758
                    }
759
                }
760

            
761
                /// Returns the configured RMT counter clock frequency.
762
                ///
763
                /// This is the actual frequency after the global RMT clock divider has been
764
                /// configured. Pulse code lengths are expressed in cycles of
765
                /// this clock divided by the channel divider configured with
766
                /// [`TxChannelConfig::with_clk_divider`] or [`RxChannelConfig::with_clk_divider`].
767
                pub fn frequency(&self) -> Rate {
768
                    self.frequency
769
                }
770
            }
771

            
772
            impl<'rmt> Rmt<'rmt, Blocking> {
773
                /// Reconfigures the driver for asynchronous operation.
774
                pub fn into_async(mut self) -> Rmt<'rmt, Async> {
775
                    self.set_interrupt_handler(chip_specific::async_interrupt_handler);
776

            
777
                    Rmt {
778
                        peripheral: self.peripheral,
779
                        frequency: self.frequency,
780
                        $(
781
                            [<channel $num>]: unsafe { self.[<channel $num>].into_async() },
782
                        )+
783
                        _mode: PhantomData,
784
                    }
785
                }
786
            }
787

            
788
            #[allow(clippy::no_effect)]
789
            const NUM_CHANNELS: usize = const { 0 $( + {$num; 1} )+ };
790
        }
791
    };
792

            
793
    // Looping channel indices: Declare input/output signals and ChannelIndex
794
    // The number of Rx and Tx channels is identical for all chips.
795
    (tx $(($num:literal, $idx:literal)),+) => {
796
        paste::paste! {
797
            // Enum of valid channel indices: For the given chip, tx/rx channels for all of these indices
798
            // exist. (Note that channel index == channel number for esp32 and esp32s2, but not for other
799
            // chips.)
800
            //
801
            // This type is useful to inform the compiler of possible values of an u8 (i.e. we use this as
802
            // homemade refinement type) which allows it to elide bounds checks in register/field
803
            // accessors of the PAC even when using DynChannelAccess.
804
            //
805
            // Cf. https://github.com/rust-lang/rust/issues/109958 regarding rustc's capabilities here.
806
            #[doc(hidden)]
807
            #[derive(Clone, Copy, Debug, PartialEq, Eq)]
808
            #[cfg_attr(feature = "defmt", derive(defmt::Format))]
809
            #[repr(u8)]
810
            #[allow(unused)]
811
            pub enum ChannelIndex {
812
                $(
813
                    [<Ch $idx>] = $idx,
814
                )+
815
            }
816

            
817
            impl ChannelIndex {
818
                #[allow(clippy::no_effect)]
819
                const MAX: u8 = const { 0 $( + {$idx; 1} )+ };
820
            }
821

            
822
            const OUTPUT_SIGNALS: [gpio::OutputSignal; ChannelIndex::MAX as usize] = [
823
                $(
824
                    gpio::OutputSignal::[<RMT_SIG_ $idx>],
825
                )+
826
            ];
827

            
828
            $(
829
                impl<'ch, Dm> TxChannelCreator<'ch, Dm> for ChannelCreator<'ch, Dm, $num>
830
                where
831
                    Dm: crate::DriverMode,
832
                {
833
                    fn configure_tx(
834
                        self,
835
                        config: &TxChannelConfig,
836
                    ) -> Result<Channel<'ch, Dm, Tx>, ConfigError> {
837
                        let raw = unsafe { DynChannelAccess::conjure(ChannelIndex::[<Ch $idx>]) };
838

            
839
                        apply_tx_config(raw, config, false)?;
840

            
841
                        Ok(Channel {
842
                            raw,
843
                            _rmt: core::marker::PhantomData,
844
                            _pin_guard: PinGuard::new_unconnected(),
845
                            _mem_guard: MemoryGuard::new(raw),
846
                            _guard: self._guard,
847
                        })
848
                    }
849
                }
850
            )+
851
        }
852
    };
853

            
854
    (rx $(($num:literal, $idx:literal)),+) => {
855
        paste::paste! {
856
            const INPUT_SIGNALS: [gpio::InputSignal; ChannelIndex::MAX as usize] = [
857
                $(
858
                    gpio::InputSignal::[<RMT_SIG_ $idx>],
859
                )+
860
            ];
861

            
862
            $(
863
                impl<'ch, Dm> RxChannelCreator<'ch, Dm> for ChannelCreator<'ch, Dm, $num>
864
                where
865
                    Dm: crate::DriverMode,
866
                {
867
                    fn configure_rx(
868
                        self,
869
                        config: &RxChannelConfig,
870
                    ) -> Result<Channel<'ch, Dm, Rx>, ConfigError> {
871
                        let raw = unsafe { DynChannelAccess::conjure(ChannelIndex::[<Ch $idx>]) };
872

            
873
2
                        apply_rx_config(raw, config, false)?;
874

            
875
                        Ok(Channel {
876
                            raw,
877
                            _rmt: core::marker::PhantomData,
878
                            _pin_guard: PinGuard::new_unconnected(),
879
                            _mem_guard: MemoryGuard::new(raw),
880
                            _guard: self._guard,
881
                        })
882
2
                    }
883
                }
884
            )+
885
        }
886
    };
887
);
888

            
889
struct ChannelIndexIter(u8);
890

            
891
impl Iterator for ChannelIndexIter {
892
    type Item = ChannelIndex;
893

            
894
    fn next(&mut self) -> Option<Self::Item> {
895
        let ch_idx = self.0;
896
        if ch_idx < ChannelIndex::MAX {
897
            self.0 = ch_idx + 1;
898
            Some(unsafe { ChannelIndex::from_u8_unchecked(ch_idx) })
899
        } else {
900
            None
901
        }
902
    }
903
}
904

            
905
impl ChannelIndex {
906
    fn iter_all() -> ChannelIndexIter {
907
        ChannelIndexIter(0)
908
    }
909

            
910
    unsafe fn from_u8_unchecked(ch_idx: u8) -> Self {
911
        debug_assert!(ch_idx < ChannelIndex::MAX);
912
        unsafe { core::mem::transmute(ch_idx) }
913
    }
914
}
915

            
916
impl<'rmt> Rmt<'rmt, Blocking> {
917
    /// Creates a new RMT instance.
918
    ///
919
    /// The `frequency` parameter configures the global RMT counter clock. Use
920
    /// [`Rmt::frequency`] to retrieve the actual configured frequency when
921
    /// converting real-time pulse durations to [`PulseCode`] lengths; this is
922
    /// not necessarily the APB clock frequency.
923
    ///
924
    /// # Errors
925
    ///
926
    /// [`ConfigError::UnreachableTargetFrequency`] when the target frequency cannot be reached.
927
2
    pub fn new(peripheral: RMT<'rmt>, frequency: Rate) -> Result<Self, ConfigError> {
928
        let clk_src = ClockSource::default();
929
        let (div, counter_frequency) = self::chip_specific::validate_clock(clk_src, frequency)?;
930

            
931
        // Only create the peripheral guards after validate_clock() to avoid that this function
932
        // contains lots of code to drop them again on error.
933
        let this = Rmt::create(peripheral, counter_frequency);
934

            
935
        self::chip_specific::configure_clock(clk_src, div, counter_frequency);
936
2
        Ok(this)
937
2
    }
938

            
939
    /// Registers an interrupt handler for the RMT peripheral.
940
    ///
941
    /// Replaces any previously registered interrupt handlers.
942
    #[instability::unstable]
943
    pub fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) {
944
        for core in crate::system::Cpu::other() {
945
            crate::interrupt::disable(core, Interrupt::RMT);
946
        }
947
        crate::interrupt::bind_handler(Interrupt::RMT, handler);
948
    }
949
}
950

            
951
impl crate::private::Sealed for Rmt<'_, Blocking> {}
952

            
953
#[instability::unstable]
954
impl crate::interrupt::InterruptConfigurable for Rmt<'_, Blocking> {
955
    fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) {
956
        self.set_interrupt_handler(handler);
957
    }
958
}
959

            
960
// Mark the channel as used and reserve the channel RAM.
961
//
962
// If this is not possible (because a preceding channel is using the RAM, or
963
// because subsequent channels are in use so that we can't reserve the RAM),
964
// restore all state and return with an error.
965
#[inline(never)]
966
2
fn reserve_channel_memory(
967
    channel: u8,
968
    state: RmtState,
969
    memsize: MemSize,
970
) -> Result<(), ConfigError> {
971
2
    if memsize.blocks() == 0 || memsize.blocks() > NUM_CHANNELS as u8 - channel {
972
        return Err(ConfigError::MemsizeOutOfRange);
973
    }
974

            
975
    let mut next_state = state;
976
    for cur_channel in channel..channel + memsize.blocks() {
977
        if RmtState::compare_exchange(
978
            cur_channel,
979
            RmtState::Unconfigured,
980
            next_state,
981
            Ordering::Acquire,
982
            Ordering::Relaxed,
983
        )
984
        .is_err()
985
        {
986
            RmtState::store_range_rev(
987
                RmtState::Unconfigured,
988
                channel..cur_channel,
989
                Ordering::Relaxed,
990
            );
991

            
992
            return Err(ConfigError::MemoryBlockNotAvailable);
993
        }
994

            
995
        // Set the first channel to `state` (`Rx`|`Tx`), the remaining (if any) to
996
        // `Reserved`
997
        next_state = RmtState::Reserved;
998
    }
999

            
    Ok(())
2
}
#[inline(never)]
fn release_channel_memory<Dir: Direction>(raw: DynChannelAccess<Dir>) {
    let channel = raw.channel();
    let memsize = raw.memsize().blocks();
    raw.set_memsize(MemSize::from_blocks(0));
    RmtState::store_range_rev(
        RmtState::Unconfigured,
        channel..channel + memsize,
        Ordering::Release,
    );
}
// We store values of type `RmtState` in the global `STATE`. However, we also need atomic access,
// thus the enum needs to be represented as AtomicU8. Thus, we end up with unsafe conversions
// between `RmtState` and `u8` to avoid constant range checks.
// This submodule wraps all accesses in a safe API which ensures that only valid enum values can
// be stored to `STATE` such that other code in this driver does not need to be concerned with
// these details.
mod state {
    use core::ops::Range;
    use portable_atomic::{AtomicU8, Ordering};
    use super::{Direction, DynChannelAccess, NUM_CHANNELS};
    static STATE: [AtomicU8; NUM_CHANNELS] =
        [const { AtomicU8::new(RmtState::Unconfigured as u8) }; NUM_CHANNELS];
    #[derive(Copy, Clone, PartialEq, Eq)]
    #[repr(u8)]
    pub(super) enum RmtState {
        // The channel is not configured for either rx or tx, and its memory is available
        Unconfigured,
        // The channels is not in use, but one of the preceding channels is using its memory
        Reserved,
        // The channel is configured for rx
        Rx,
        // The channel is configured for tx
        Tx,
    }
    impl RmtState {
        /// Returns whether this state corresponds to a rx, tx, or other configuration.
        #[allow(unused)]
        #[inline]
        pub(super) fn is_tx(&self) -> Option<bool> {
            match self {
                Self::Rx => Some(false),
                Self::Tx => Some(true),
                _ => None,
            }
        }
        /// Converts a `u8` to `Self` without checking that it has a valid value for the enum.
        ///
        /// # Safety
        ///
        /// - Must only be called with valid values of the RmtState discriminant
        #[allow(unused)]
        #[inline]
        unsafe fn from_u8_unchecked(value: u8) -> Self {
            unsafe { core::mem::transmute::<_, Self>(value) }
        }
        /// Loads channel state from the global `STATE` by channel index.
        ///
        /// # Safety
        ///
        /// - the `channel` number must be in 0..NUM_CHANNELS
        #[allow(unused)]
        #[inline]
        pub(super) unsafe fn load_by_channel_number(channel: u8, ordering: Ordering) -> Self {
            unsafe { Self::from_u8_unchecked(STATE[channel as usize].load(ordering)) }
        }
        /// Stores the given state to all channel states for an index range in reverse order.
        #[inline]
        pub(super) fn store_range_rev(self, range: Range<u8>, ordering: Ordering) {
            for ch_num in range.rev() {
                STATE[ch_num as usize].store(self as u8, ordering);
            }
        }
        /// Stores channel state to the global `STATE` given a `DynChannelAccess`.
        #[allow(unused)]
        #[inline]
        pub(super) fn store<Dir: Direction>(self, raw: DynChannelAccess<Dir>, ordering: Ordering) {
            STATE[raw.channel() as usize].store(self as u8, ordering);
        }
        /// Loads channel state from the global `STATE` given a `DynChannelAccess`.
        #[allow(unused)]
        #[inline]
        pub(super) fn load<Dir: Direction>(raw: DynChannelAccess<Dir>, ordering: Ordering) -> Self {
            // SAFETY: The only implementations of RawChannelAccess are in this module and can only
            // be (safely) constructed using valid channel numbers.
            unsafe { Self::load_by_channel_number(raw.channel(), ordering) }
        }
        /// Performs a compare_exchange on the global `STATE` by channel index.
        #[inline]
        pub(super) fn compare_exchange(
            ch_num: u8,
            current: Self,
            new: Self,
            success: Ordering,
            failure: Ordering,
        ) -> Result<Self, Self> {
2
            STATE[ch_num as usize]
                .compare_exchange(current as u8, new as u8, success, failure)
                .map(|prev| unsafe { Self::from_u8_unchecked(prev) })
                .map_err(|prev| unsafe { Self::from_u8_unchecked(prev) })
        }
    }
}
use state::RmtState;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct RmtClockGuard;
impl RmtClockGuard {
    fn new() -> Self {
        #[cfg(soc_has_clock_node_rmt_sclk)]
        clocks::ClockTree::with(|clocks| {
8
            if clocks::RmtInstance::Rmt.sclk_config(clocks).is_none() {
2
                clocks::RmtInstance::Rmt.configure_sclk(clocks, ClockSource::default());
            }
6
            clocks::RmtInstance::Rmt.request_sclk(clocks);
        });
        Self
    }
}
impl Drop for RmtClockGuard {
6
    fn drop(&mut self) {
        #[cfg(soc_has_clock_node_rmt_sclk)]
6
        clocks::ClockTree::with(|clocks| clocks::RmtInstance::Rmt.release_sclk(clocks));
6
    }
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct ChannelGuards {
    _peripheral: GenericPeripheralGuard<{ crate::system::Peripheral::Rmt as u8 }>,
    _clock: RmtClockGuard,
}
impl ChannelGuards {
8
    fn new() -> Self {
        Self {
            _peripheral: GenericPeripheralGuard::new(),
            _clock: RmtClockGuard::new(),
        }
8
    }
}
/// RMT Channel.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Channel<'ch, Dm, Dir>
where
    Dm: crate::DriverMode,
    Dir: Direction,
{
    raw: DynChannelAccess<Dir>,
    // Holds the lifetime for which have unique access to both the channel and the pin its
    // configured for. Conceptually, for 'ch, we keep the Rmt peripheral alive.
    _rmt: PhantomData<Rmt<'ch, Dm>>,
    _pin_guard: PinGuard,
    _mem_guard: MemoryGuard<Dir>,
    // Only the "outermost" Channel/ChannelCreator holds these guards, which avoids constant
    // inc/dec of the reference counts on reborrow and drop.
    _guard: Option<ChannelGuards>,
}
// The reborrowing API treats Channel similar to a smart pointer: Ensure that it's size is actually
// in line with that notion.
const _: () = if core::mem::size_of::<Channel<'static, Blocking, Tx>>() > 4 {
    core::panic!("Channel growing too large!");
};
/// Per-channel size of the RMT hardware buffer (number of `PulseCode`s).
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");
#[inline(never)]
fn apply_tx_config(
    raw: DynChannelAccess<Tx>,
    config: &TxChannelConfig,
    reconfigure: bool,
) -> Result<(), ConfigError> {
    let memsize = MemSize::from_blocks(config.memsize);
    if reconfigure {
        release_channel_memory(raw);
    }
    reserve_channel_memory(raw.channel(), RmtState::Tx, memsize)?;
    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);
    Ok(())
}
#[inline(never)]
2
fn apply_rx_config(
    raw: DynChannelAccess<Rx>,
    config: &RxChannelConfig,
    reconfigure: bool,
) -> Result<(), ConfigError> {
    #[cfg_attr(any(esp32, esp32s2), allow(clippy::absurd_extreme_comparisons))]
2
    if config.idle_threshold > MAX_RX_IDLE_THRESHOLD {
        return Err(ConfigError::IdleThresholdOutOfRange);
    }
2
    let memsize = MemSize::from_blocks(config.memsize);
2
    if reconfigure {
        release_channel_memory(raw);
    }
2
    reserve_channel_memory(raw.channel(), RmtState::Rx, memsize)?;
2
    raw.set_divider(config.clk_divider);
    raw.set_rx_carrier(
        config.carrier_modulation,
        config.carrier_high,
2
        config.carrier_low,
        config.carrier_level,
    );
2
    raw.set_rx_filter_threshold(config.filter_threshold);
    raw.set_rx_idle_threshold(config.idle_threshold);
    raw.set_memsize(memsize);
    Ok(())
2
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct MemoryGuard<Dir: Direction> {
    raw: Option<DynChannelAccess<Dir>>,
    _dir: PhantomData<Dir>,
}
impl<Dir: Direction> MemoryGuard<Dir> {
    fn new(raw: DynChannelAccess<Dir>) -> Self {
        Self {
            raw: Some(raw),
            _dir: PhantomData,
        }
    }
    fn new_empty() -> Self {
        Self {
            raw: None,
            _dir: PhantomData,
        }
    }
}
impl<Dir: Direction> Drop for MemoryGuard<Dir> {
    fn drop(&mut self) {
        if let Some(raw) = self.raw {
            release_channel_memory(raw);
        }
    }
}
impl<Dm, Dir> Channel<'_, Dm, Dir>
where
    Dm: crate::DriverMode,
    Dir: Direction,
{
    /// Reborrows this channel for a shorter lifetime `'a`.
    pub fn reborrow<'a>(&'a mut self) -> Channel<'a, Dm, Dir> {
        Channel {
            raw: self.raw,
            _rmt: self._rmt,
            // Resources must only be released once the parent is dropped.
            _pin_guard: PinGuard::new_unconnected(),
            _mem_guard: MemoryGuard::new_empty(),
            _guard: None,
        }
    }
}
impl<'ch, Dm> Channel<'ch, Dm, Tx>
where
    Dm: crate::DriverMode,
{
    /// Connects a pin to the channel's output signal.
    ///
    /// This will replace previous pin assignments for this signal.
    pub fn with_pin(mut self, pin: impl PeripheralOutput<'ch>) -> Self {
        let pin = pin.into();
        // Make sure to not cause a pulse between enabling the output and connecting it.
        let (_, level) = self.raw.tx_idle_output();
        pin.set_output_high(level.into());
        pin.apply_output_config(&OutputConfig::default());
        pin.set_output_enable(true);
        self._pin_guard = pin.connect_with_guard(self.raw.output_signal());
        self
    }
    /// Changes the configuration.
    ///
    /// # Errors
    ///
    /// [`ConfigError::MemoryBlockNotAvailable`] or [`ConfigError::MemsizeOutOfRange`].
    pub fn apply_config(&mut self, config: &TxChannelConfig) -> Result<(), ConfigError> {
        apply_tx_config(self.raw, config, true)
    }
}
impl<'ch, Dm> Channel<'ch, Dm, Rx>
where
    Dm: crate::DriverMode,
{
    /// Connects a pin to the channel's input signal.
    ///
    /// This will replace previous pin assignments for this signal.
    pub fn with_pin(self, pin: impl PeripheralInput<'ch>) -> Self {
2
        let pin = pin.into();
        pin.apply_input_config(&InputConfig::default());
        pin.set_input_enable(true);
        self.raw.input_signal().connect_to(&pin);
        self
    }
    /// Changes the configuration.
    ///
    /// # Errors
    ///
    /// [`ConfigError::MemoryBlockNotAvailable`], [`ConfigError::MemsizeOutOfRange`], or
    /// [`ConfigError::IdleThresholdOutOfRange`].
    pub fn apply_config(&mut self, config: &RxChannelConfig) -> Result<(), ConfigError> {
        apply_rx_config(self.raw, config, true)
    }
}
/// TX channel creator for the RMT peripheral.
pub trait TxChannelCreator<'ch, Dm>
where
    Dm: crate::DriverMode,
{
    /// Configures the TX channel.
    ///
    /// # Errors
    ///
    /// [`ConfigError::MemoryBlockNotAvailable`] or [`ConfigError::MemsizeOutOfRange`] under the
    /// same conditions as [`Channel<Tx>::apply_config`].
    fn configure_tx(self, config: &TxChannelConfig) -> Result<Channel<'ch, Dm, Tx>, ConfigError>
    where
        Self: Sized;
}
/// RX channel creator for the RMT peripheral.
pub trait RxChannelCreator<'ch, Dm>
where
    Dm: crate::DriverMode,
{
    /// Configures the RX channel.
    ///
    /// # Errors
    ///
    /// [`ConfigError::MemoryBlockNotAvailable`], [`ConfigError::MemsizeOutOfRange`], or
    /// [`ConfigError::IdleThresholdOutOfRange`] under the same conditions as
    /// [`Channel<Rx>::apply_config`].
    fn configure_rx(self, config: &RxChannelConfig) -> Result<Channel<'ch, Dm, Rx>, ConfigError>
    where
        Self: Sized;
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct TxGuard {
    raw: Option<DynChannelAccess<Tx>>,
}
impl TxGuard {
    fn new(raw: DynChannelAccess<Tx>) -> Self {
        Self { raw: Some(raw) }
    }
    fn new_empty() -> Self {
        Self { raw: None }
    }
    /// Indicates that the transaction has completed (or was never started) and does not require
    /// explicit stopping on drop.
    fn set_completed(&mut self) {
        self.raw = None;
    }
    fn is_active(&self) -> bool {
        self.raw.is_some()
    }
}
impl Drop for TxGuard {
    fn drop(&mut self) {
        if let Some(raw) = self.raw {
            if !matches!(raw.get_tx_status(), Some(Event::Error | Event::End)) {
                raw.stop_tx();
                raw.update();
                // Block until the channel is safe to use again.
                #[cfg(not(rmt_has_tx_immediate_stop))]
                while !matches!(raw.get_tx_status(), Some(Event::Error | Event::End)) {}
            }
            raw.unlisten_tx_interrupt(EnumSet::all());
        }
    }
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct RxGuard {
    raw: Option<DynChannelAccess<Rx>>,
}
impl RxGuard {
    fn new(raw: DynChannelAccess<Rx>) -> Self {
        Self { raw: Some(raw) }
    }
    fn new_empty() -> Self {
        Self { raw: None }
    }
    /// Indicates that the transaction has completed (or was never started) and does not require
    /// explicit stopping on drop.
    fn set_completed(&mut self) {
        self.raw = None;
    }
}
impl Drop for RxGuard {
    fn drop(&mut self) {
        if let Some(raw) = self.raw {
            raw.stop_rx(true);
            raw.update();
            raw.unlisten_rx_interrupt(EnumSet::all());
        }
    }
}
/// An in-progress transaction for a single shot TX transaction.
///
/// If the data size exceeds the size of the internal buffer, `.poll()` or
/// `.wait()` needs to be called before the entire buffer has been sent to avoid
/// underruns.
#[must_use = "transactions need to be `poll()`ed / `wait()`ed for to ensure progress"]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TxTransaction<'ch, 'data> {
    // This must go first such that it is dropped before the channel (which might disable the
    // peripheral on drop)!
    _guard: TxGuard,
    channel: Channel<'ch, Blocking, Tx>,
    writer: RmtWriter,
    // Remaining data that has not yet been written to channel RAM. May be empty.
    remaining_data: &'data [PulseCode],
}
impl<'ch> TxTransaction<'ch, '_> {
    #[cfg_attr(place_rmt_driver_in_ram, ram)]
    fn poll_internal(&mut self) -> Option<Event> {
        let raw = self.channel.raw;
        let status = raw.get_tx_status();
        if status == Some(Event::Threshold) {
            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.
            self.writer.write(&mut self.remaining_data, raw, false);
        }
        status
    }
    /// Checks transmission status and writes new data to the hardware if
    /// necessary.
    ///
    /// Returns whether transmission has ended (whether successfully or with an
    /// error). In that case, a subsequent call to `wait()` returns immediately.
    #[cfg_attr(place_rmt_driver_in_ram, inline(always))]
    pub fn poll(&mut self) -> bool {
        matches!(self.poll_internal(), Some(Event::Error | Event::End))
    }
    /// Waits for the transaction to complete.
    #[cfg_attr(place_rmt_driver_in_ram, inline(always))]
    pub fn wait(
        mut self,
    ) -> Result<Channel<'ch, Blocking, Tx>, (Error, Channel<'ch, Blocking, Tx>)> {
        // Not sure that all the error cases below can happen. However, it's best to
        // handle them to be sure that we don't lock up here in case they can happen.
        let result = loop {
            match self.poll_internal() {
                Some(Event::Error) => break Err(Error::TransmissionError),
                Some(Event::End) => break self.writer.state().to_result(),
                _ => continue,
            }
        };
        self._guard.set_completed();
        match result {
            Ok(()) => Ok(self.channel),
            Err(err) => Err((err, self.channel)),
        }
    }
}
/// An in-progress continuous TX transaction.
#[must_use = "transactions will be aborted when dropped"]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ContinuousTxTransaction<'ch> {
    // This must go first such that it is dropped before the channel (which might disable the
    // peripheral on drop)!
    _guard: TxGuard,
    channel: Channel<'ch, Blocking, Tx>,
}
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 TxTransaction!
    /// Stops transaction when the current iteration ends.
    #[cfg_attr(place_rmt_driver_in_ram, inline(always))]
    pub fn stop_next(
        self,
    ) -> Result<Channel<'ch, Blocking, Tx>, (Error, Channel<'ch, Blocking, Tx>)> {
        self.stop_impl(false)
    }
    /// Stops transaction as soon as possible.
    #[cfg_attr(place_rmt_driver_in_ram, inline(always))]
    pub fn stop(self) -> Result<Channel<'ch, Blocking, Tx>, (Error, Channel<'ch, Blocking, Tx>)> {
        self.stop_impl(true)
    }
    #[cfg_attr(place_rmt_driver_in_ram, ram)]
    fn stop_impl(
        mut self,
        immediate: bool,
    ) -> Result<Channel<'ch, Blocking, Tx>, (Error, Channel<'ch, Blocking, Tx>)> {
        let raw = self.channel.raw;
        let mut result = Ok(());
        if self._guard.is_active() {
            // If rmt_has_tx_loop_auto_stop and the engine is stopped already, this is not
            // necessary. However, explicitly stopping unconditionally makes the logic
            // here much simpler and shouldn't create much overhead.
            raw.set_tx_continuous(false);
            if immediate {
                raw.stop_tx()
            }
            raw.update();
            if !immediate || !cfg!(rmt_has_tx_immediate_stop) {
                loop {
                    match raw.get_tx_status() {
                        Some(Event::Error) => {
                            result = Err(Error::TransmissionError);
                            break;
                        }
                        Some(Event::End) => break,
                        Some(Event::LoopCount) if cfg!(rmt_has_tx_loop_auto_stop) => break,
                        _ => continue,
                    }
                }
            }
        }
        self._guard.set_completed();
        match result {
            Ok(()) => Ok(self.channel),
            Err(err) => Err((err, self.channel)),
        }
    }
    /// Returns whether the `loopcount` interrupt bit is set.
    ///
    /// Whether this implies that the transmission has stopped depends on the [`LoopMode`] value
    /// provided when starting it.
    #[cfg(rmt_has_tx_loop_count)]
    pub fn is_loopcount_interrupt_set(&self) -> bool {
        !self._guard.is_active() || self.channel.raw.is_tx_loopcount_interrupt_set()
    }
}
/// RMT Channel Creator.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ChannelCreator<'ch, Dm, const CHANNEL: u8>
where
    Dm: crate::DriverMode,
{
    // Conceptually, this retains a reference to the main driver struct, even when the
    // `ChannelCreator` is taken out of it and the `Rmt` dropped. This prevents re-inititalizing
    // the `Rmt` and obtaining a duplicate `ChannelCreator`.
    _rmt: PhantomData<Rmt<'ch, Dm>>,
    // We need to keep the peripheral and source clocks alive since the following sequence of
    // events is possible:
    //
    // ```
    // let cc = {
    //     let rmt = Rmt::new(peripheral, freq);  // 1
    //     rmt.channel0  // 2
    // };  // 3 (drop other ChannelCreators and Rmt.peripheral)
    // let ch0 = cc.configure_tx(pin, config).unwrap();  // 4 (create Channel._guard)
    // ch0.transmit(...);  // 5
    // ```
    //
    // If there was no _guard in ChannelCreator, the peripheral would be disabled in step 3, and
    // re-enabled in step 4, losing the clock configuration that was set in step 1.
    _guard: Option<ChannelGuards>,
}
impl<'ch, Dm, const CHANNEL: u8> ChannelCreator<'ch, Dm, CHANNEL>
where
    Dm: crate::DriverMode,
{
    fn conjure() -> Self {
        Self {
            _rmt: PhantomData,
2
            _guard: Some(ChannelGuards::new()),
        }
    }
    unsafe fn into_async(self) -> ChannelCreator<'ch, Async, CHANNEL> {
        ChannelCreator {
            _rmt: PhantomData,
            _guard: self._guard,
        }
    }
    /// Reborrows this channel creator for a shorter lifetime `'a`.
    pub fn reborrow<'a>(&'a mut self) -> ChannelCreator<'a, Dm, CHANNEL> {
        Self {
            _rmt: PhantomData,
            // Resources must only be released once the parent is dropped.
            _guard: None,
        }
    }
    /// Unsafely steal a channel creator instance.
    ///
    /// # Safety
    ///
    /// Circumvents HAL ownership and safety guarantees and allows creating
    /// multiple handles to the same peripheral structure.
    #[inline]
    pub unsafe fn steal() -> Self {
        Self {
            _rmt: PhantomData,
            _guard: Some(ChannelGuards::new()),
        }
    }
}
/// Loop mode for continuous transmission.
///
/// Depending on hardware support, the `loopcount` interrupt and automatic stopping of the
/// transmission upon reaching a specified loop count may not be available.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum LoopMode {
    /// Repeats until explicitly stopped.
    Infinite,
    // FIXME: Does continuous tx trigger the End interrupt on each repetition such that it could
    // be used to emulate the LoopCount interrupt on devices that lack it?
    /// Repeats until explicitly stopped, and asserts the loop count interrupt upon completing the
    /// given number of iterations.
    ///
    /// Loop counts larger than [`MAX_TX_LOOPCOUNT`] will result in an error.
    #[cfg(rmt_has_tx_loop_count)]
    InfiniteWithInterrupt(u16),
    /// Repeats for the given number of iterations, and also sets the loop count interrupt flag upon
    /// completion.
    ///
    /// If the iteration count is 0, the transaction will complete immediately without
    /// starting the transmitter.
    ///
    /// Loop counts larger than [`MAX_TX_LOOPCOUNT`] will result in an error.
    #[cfg(rmt_has_tx_loop_auto_stop)]
    Finite(u16),
}
impl LoopMode {
    #[allow(unused)]
    fn get_count(self) -> u16 {
        match self {
            Self::Infinite => 0,
            #[cfg(rmt_has_tx_loop_count)]
            Self::InfiniteWithInterrupt(count) => count,
            #[cfg(rmt_has_tx_loop_auto_stop)]
            Self::Finite(count) => count,
        }
    }
}
/// Channel in TX mode.
impl<'ch> Channel<'ch, Blocking, Tx> {
    /// Starts transmitting the given pulse code sequence.
    /// Returns a [`TxTransaction`] that 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>(
        self,
        mut data: &'data [PulseCode],
    ) -> Result<TxTransaction<'ch, 'data>, (Error, Self)> {
        let raw = self.raw;
        let memsize = raw.memsize();
        let mut writer = RmtWriter::new();
        writer.write(&mut data, raw, true);
        if let WriterState::Error(e) = writer.state() {
            return Err((e, self));
        }
        raw.clear_tx_interrupts(EnumSet::all());
        raw.start_send(None, memsize);
        Ok(TxTransaction {
            channel: self,
            writer,
            remaining_data: data,
            _guard: TxGuard::new(raw),
        })
    }
    /// Starts transmitting the given pulse code continuously.
    ///
    /// Returns a [`ContinuousTxTransaction`] that can be used to stop the ongoing
    /// transmission and get back the channel for further use.
    ///
    /// The `mode` argument determines whether transmission will continue until explicitly stopped
    /// or for a fixed number of iterations; see [`LoopMode`] for more details.
    #[cfg_attr(
        rmt_has_tx_loop_count,
        doc = "When using a loop `mode` other than [`LoopMode::Infinite`], [`ContinuousTxTransaction::is_loopcount_interrupt_set`] can be used to check if the loop count is reached."
    )]
    /// The length of `data` cannot exceed the size of the allocated RMT RAM.
    #[cfg_attr(place_rmt_driver_in_ram, ram)]
    pub fn transmit_continuously(
        self,
        mut data: &[PulseCode],
        mode: LoopMode,
    ) -> Result<ContinuousTxTransaction<'ch>, (Error, Self)> {
        let raw = self.raw;
        let memsize = raw.memsize();
        #[cfg(rmt_has_tx_loop_count)]
        if mode.get_count() > MAX_TX_LOOPCOUNT {
            return Err((Error::InvalidArgument, self));
        }
        let mut writer = RmtWriter::new();
        writer.write(&mut data, raw, true);
        match writer.state() {
            WriterState::Error(e) => return Err((e, self)),
            WriterState::Active => return Err((Error::Overflow, self)),
            WriterState::Done => (),
        }
        let mut _guard = TxGuard::new(raw);
        #[cfg(rmt_has_tx_loop_auto_stop)]
        if mode == LoopMode::Finite(0) {
            _guard.set_completed();
        }
        if _guard.is_active() {
            raw.clear_tx_interrupts(EnumSet::all());
            raw.start_send(Some(mode), memsize);
        }
        Ok(ContinuousTxTransaction {
            channel: self,
            _guard,
        })
    }
}
/// RX transaction instance.
#[must_use = "transactions need to be `poll()`ed / `wait()`ed for to ensure progress"]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RxTransaction<'ch, 'data> {
    // This must go first such that it is dropped before the channel (which might disable the
    // peripheral on drop)!
    _guard: RxGuard,
    channel: Channel<'ch, Blocking, Rx>,
    reader: RmtReader,
    data: &'data mut [PulseCode],
}
impl<'ch> RxTransaction<'ch, '_> {
    #[cfg_attr(place_rmt_driver_in_ram, ram)]
2424937
    fn poll_internal(&mut self) -> Option<Event> {
2424937
        let raw = self.channel.raw;
        let status = raw.get_rx_status();
        match status {
            // Read all available data also on error
            Some(Event::End | Event::Error) => {
                // Do not clear the interrupt flags here: Subsequent calls of wait() must
                // be able to observe them if this is currently called via poll()
                // Rx is stopped already, but we do need to clear the rx enable flag and
                // apply that right away! Otherwise the next `raw.update` call would do so
                // while starting the next transaction, which breaks it when SCLK is slow.
                raw.stop_rx(false);
                raw.update();
                // `RmtReader::read()` is safe to call even if `poll_internal` is called repeatedly
                // after the receiver finished since it returns immediately if already done.
                if !self.reader.read(&mut self.data, raw, true) {
                    return Some(Event::Error);
                }
            }
            #[cfg(rmt_has_rx_wrap)]
            Some(Event::Threshold) => {
                raw.clear_rx_interrupts(Event::Threshold);
                if self.reader.state == ReaderState::Active {
                    self.reader.read(&mut self.data, raw, false);
                }
            }
            _ => (),
        }
        status
2424936
    }
    /// Checks receive status.
    ///
    /// Returns whether reception has ended (whether successfully or with an
    /// error). In that case, a subsequent call to `wait()` returns immediately.
    #[cfg_attr(place_rmt_driver_in_ram, inline(always))]
    pub fn poll(&mut self) -> bool {
        matches!(self.poll_internal(), Some(Event::Error | Event::End))
    }
    /// Waits for the transaction to complete.
    #[cfg_attr(place_rmt_driver_in_ram, inline(always))]
    // The return type isn't nice, but the blocking API needs a broader redesign anyway.
    #[allow(clippy::type_complexity)]
2
    pub fn wait(
        mut self,
    ) -> Result<(usize, Channel<'ch, Blocking, Rx>), (Error, Channel<'ch, Blocking, Rx>)> {
        let result = loop {
2424937
            match self.poll_internal() {
                Some(Event::Error) => break Err(Error::ReceiverError),
                Some(Event::End) => break Ok(self.reader.total),
                _ => continue,
            }
        };
        self._guard.set_completed();
        match result {
            Ok(total) => Ok((total, self.channel)),
            Err(err) => Err((err, self.channel)),
        }
    }
}
/// Channel is RX mode.
impl<'ch> Channel<'ch, Blocking, Rx> {
    #[procmacros::doc_replace(
        "rx_size_limit" => {
            cfg(any(esp32, esp32s2)) => "The length of the received data cannot exceed the allocated RMT RAM.",
            _ => ""
        }
    )]
    /// Starts receiving pulse codes into the given buffer.
    /// Returns a [`RxTransaction`] that can be used to wait for receive to complete
    /// and get back the channel for further use.
    ///
    /// # {rx_size_limit}
    #[cfg_attr(place_rmt_driver_in_ram, ram)]
2
    pub fn receive<'data>(
        self,
        data: &'data mut [PulseCode],
    ) -> Result<RxTransaction<'ch, 'data>, (Error, Self)> {
        let raw = self.raw;
        let memsize = raw.memsize();
        if !property!("rmt.has_rx_wrap") && data.len() > memsize.codes() {
            return Err((Error::InvalidDataLength, self));
        }
        let reader = RmtReader::new();
        raw.clear_rx_interrupts(EnumSet::all());
        raw.start_receive(true, memsize);
2
        Ok(RxTransaction {
            channel: self,
            reader,
            data,
            _guard: RxGuard::new(raw),
        })
2
    }
}
static WAKER: [AtomicWaker; NUM_CHANNELS] = [const { AtomicWaker::new() }; NUM_CHANNELS];
// Lock to synchronize access to registers that are shared between channels.
static RMT_LOCK: RawMutex = RawMutex::new();
#[must_use = "futures do nothing unless you `.await` or poll them"]
struct TxFuture<'a> {
    raw: DynChannelAccess<Tx>,
    _phantom: PhantomData<Channel<'a, Async, Tx>>,
    writer: RmtWriter,
    // Remaining data that has not yet been written to channel RAM. May be empty.
    data: &'a [PulseCode],
    _guard: TxGuard,
}
impl core::future::Future for TxFuture<'_> {
    type Output = Result<(), Error>;
    #[cfg_attr(place_rmt_driver_in_ram, ram)]
    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.get_mut();
        let raw = this.raw;
        if let WriterState::Error(err) = this.writer.state() {
            return Poll::Ready(Err(err));
        }
        WAKER[raw.channel() as usize].register(ctx.waker());
        let result = match raw.get_tx_status() {
            Some(Event::Error) => Err(Error::TransmissionError),
            Some(Event::End) => this.writer.state().to_result(),
            Some(Event::Threshold) => {
                raw.clear_tx_interrupts(Event::Threshold);
                this.writer.write(&mut this.data, raw, false);
                if this.writer.state() == WriterState::Active {
                    raw.listen_tx_interrupt(Event::Threshold);
                }
                return Poll::Pending;
            }
            _ => return Poll::Pending,
        };
        this._guard.set_completed();
        Poll::Ready(result)
    }
}
/// TX channel in async mode.
impl Channel<'_, Async, Tx> {
    /// Starts transmitting the given pulse code sequence.
    #[cfg_attr(place_rmt_driver_in_ram, ram)]
    pub fn transmit(&mut self, mut data: &[PulseCode]) -> impl Future<Output = Result<(), Error>> {
        let raw = self.raw;
        let memsize = raw.memsize();
        let mut writer = RmtWriter::new();
        writer.write(&mut data, raw, true);
        let _guard = if writer.state().is_ok() {
            let wrap = match writer.state() {
                WriterState::Error(_) => false,
                WriterState::Active => true,
                WriterState::Done => false,
            };
            raw.clear_tx_interrupts(EnumSet::all());
            let mut events = Event::End | Event::Error;
            if wrap {
                events |= Event::Threshold;
            }
            raw.listen_tx_interrupt(events);
            raw.start_send(None, memsize);
            TxGuard::new(raw)
        } else {
            TxGuard::new_empty()
        };
        TxFuture {
            raw,
            _phantom: PhantomData,
            writer,
            data,
            _guard,
        }
    }
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
struct RxFuture<'a> {
    raw: DynChannelAccess<Rx>,
    _phantom: PhantomData<Channel<'a, Async, Rx>>,
    reader: RmtReader,
    data: &'a mut [PulseCode],
    _guard: RxGuard,
}
impl core::future::Future for RxFuture<'_> {
    type Output = Result<usize, Error>;
    #[cfg_attr(place_rmt_driver_in_ram, ram)]
    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.get_mut();
        let raw = this.raw;
        if let ReaderState::Error(err) = this.reader.state {
            return Poll::Ready(Err(err));
        }
        WAKER[raw.channel() as usize].register(ctx.waker());
        let result = match raw.get_rx_status() {
            // Read all available data also on error
            Some(ev @ (Event::End | Event::Error)) => {
                // Rx is stopped already, but we do need to clear the rx enable flag and
                // apply that right away! Otherwise the next `raw.update` call would do so
                // while starting the next transaction, which breaks it when SCLK is slow.
                raw.stop_rx(false);
                raw.update();
                if !this.reader.read(&mut this.data, raw, true) {
                    Err(Error::ReceiverError)
                } else {
                    match ev {
                        Event::Error => Err(Error::ReceiverError),
                        _ => Ok(this.reader.total),
                    }
                }
            }
            #[cfg(rmt_has_rx_wrap)]
            Some(Event::Threshold) => {
                raw.clear_rx_interrupts(Event::Threshold);
                this.reader.read(&mut this.data, raw, false);
                if this.reader.state == ReaderState::Active {
                    raw.listen_rx_interrupt(Event::Threshold);
                }
                return Poll::Pending;
            }
            _ => return Poll::Pending,
        };
        this._guard.set_completed();
        Poll::Ready(result)
    }
}
/// RX channel in async mode.
impl Channel<'_, Async, Rx> {
    #[procmacros::doc_replace(
        "rx_size_limit" => {
            cfg(any(esp32, esp32s2)) => "The length of the received data cannot exceed the allocated RMT RAM.",
            _ => ""
        }
    )]
    /// Starts receiving a pulse code sequence.
    ///
    /// # {rx_size_limit}
    #[cfg_attr(place_rmt_driver_in_ram, ram)]
    pub fn receive(
        &mut self,
        data: &mut [PulseCode],
    ) -> impl Future<Output = Result<usize, Error>> {
        let raw = self.raw;
        let memsize = raw.memsize();
        let mut reader = RmtReader::new();
        let _guard = if !property!("rmt.has_rx_wrap") && data.len() > memsize.codes() {
            reader.state = ReaderState::Error(Error::InvalidDataLength);
            RxGuard::new_empty()
        } else {
            raw.clear_rx_interrupts(EnumSet::all());
            raw.listen_rx_interrupt(Event::End | Event::Error | Event::Threshold);
            raw.start_receive(true, memsize);
            RxGuard::new(raw)
        };
        RxFuture {
            raw,
            reader,
            data,
            _phantom: PhantomData,
            _guard,
        }
    }
}
#[derive(Debug, EnumSetType)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum Event {
    Error,
    Threshold,
    End,
    LoopCount,
}
impl<Dir: Direction> DynChannelAccess<Dir> {
    #[inline]
    fn channel_ram_start_offset(self) -> usize {
        usize::from(self.channel()) * property!("rmt.channel_ram_size")
    }
    #[inline]
    fn channel_ram_start(self) -> *mut PulseCode {
        unsafe {
            (property!("rmt.ram_start") as *mut PulseCode).add(self.channel_ram_start_offset())
        }
    }
}
impl DynChannelAccess<Tx> {
    #[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(always)]
    fn start_send(self, loopmode: Option<LoopMode>, 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_loopmode(loopmode);
        self.set_tx_wrap_mode(loopmode.is_none());
        self.update();
        self.start_tx();
        self.update();
    }
    #[inline(always)]
    fn listen_tx_interrupt(self, event: impl Into<EnumSet<Event>>) {
        self.set_tx_interrupt(event.into(), true);
    }
    #[inline(always)]
    fn unlisten_tx_interrupt(self, event: impl Into<EnumSet<Event>>) {
        self.set_tx_interrupt(event.into(), false);
    }
}
impl DynChannelAccess<Rx> {
    #[inline(always)]
    fn input_signal(self) -> gpio::InputSignal {
        INPUT_SIGNALS[self.ch_idx as usize]
    }
    #[inline(always)]
    fn start_receive(self, _wrap: bool, _memsize: MemSize) {
        #[cfg(rmt_has_rx_wrap)]
        {
2
            self.set_rx_threshold((_memsize.codes() / 2) as u16);
            self.set_rx_wrap_mode(_wrap);
            self.update();
        }
        self.start_rx();
        self.update();
    }
    #[inline(always)]
    fn listen_rx_interrupt(self, event: impl Into<EnumSet<Event>>) {
        self.set_rx_interrupt(event.into(), true);
    }
    #[inline(always)]
    fn unlisten_rx_interrupt(self, event: impl Into<EnumSet<Event>>) {
        self.set_rx_interrupt(event.into(), false);
    }
}
type ClockSource = clocks::RmtSclkConfig;
// Obtain maximum value for a register field from the PAC's register spec.
macro_rules! max_from_register_spec {
    ($typ:ty, $reg:ident, $spec:ident, $w:ident) => {{
        use crate::soc::pac::rmt::$reg as reg;
        type Spec = reg::$spec;
        const WIDTH: u8 = reg::$w::<Spec>::WIDTH;
        const _: () = if WIDTH as u32 > <$typ>::BITS {
            core::panic!("Unexpectedly large register WIDTH");
        };
        // Fits into $ty according to the assertion above
        ((1u32 << WIDTH) - 1) as $typ
    }};
}
#[cfg(not(any(esp32, esp32s2)))]
mod chip_specific {
    use enumset::EnumSet;
    use portable_atomic::{AtomicU32, Ordering};
    #[cfg(place_rmt_driver_in_ram)]
    use procmacros::ram;
    use super::{
        ChannelIndex,
        ClockSource,
        ConfigError,
        Direction,
        DynChannelAccess,
        Event,
        Level,
        LoopMode,
        MemSize,
        RMT_LOCK,
        Rx,
        Tx,
        WAKER,
    };
    use crate::{peripherals::RMT, soc::clocks, time::Rate};
    // Time to wait after a `conf_update`, see `DynChannelAccess::update`.
    static CONF_UPDATE_DELAY_US: AtomicU32 = AtomicU32::new(0);
    pub(super) fn validate_clock(
        source: ClockSource,
        frequency: Rate,
    ) -> Result<(u8, Rate), ConfigError> {
        let src_clock = Rate::from_hz(clocks::RmtInstance::sclk_source_frequency(source));
2
        if frequency > src_clock {
            return Err(ConfigError::UnreachableTargetFrequency);
        }
        let div = src_clock
            .as_hz()
            .checked_div(frequency.as_hz())
            .ok_or(ConfigError::UnreachableTargetFrequency)?;
2
        let div = u8::try_from(div - 1).map_err(|_| ConfigError::UnreachableTargetFrequency)?;
        let actual_frequency = src_clock / (u32::from(div) + 1);
        Ok((div, actual_frequency))
    }
    pub(super) fn configure_clock(source: ClockSource, div: u8, frequency: Rate) {
        // Synchronizing a `conf_update` into the SCLK domain takes about one SCLK cycle, wait for
        // two to leave some margin. For a fast SCLK, this is shorter than the register accesses
        // following the update, so don't delay at all.
2
        let delay_us = if frequency < Rate::from_mhz(10) {
            2_000_000u32.div_ceil(frequency.as_hz())
        } else {
            0
        };
        CONF_UPDATE_DELAY_US.store(delay_us, Ordering::Relaxed);
        clocks::ClockTree::with(|clocks| {
2
            clocks::RmtInstance::Rmt.configure_sclk(clocks, source);
        });
        // TODO: configure dividers through clock tree
        cfg_select! {
            esp32p4 => {
                use crate::peripherals::HP_SYS_CLKRST;
                HP_SYS_CLKRST::regs()
                    .peri_clk_ctrl22()
                    .modify(|_, w| unsafe {
                        w.rmt_clk_div_num().bits(div);
                        w.rmt_clk_div_numerator().bits(0);
                        w.rmt_clk_div_denominator().bits(0)
                    });
            }
            esp32s31 => {
                use crate::peripherals::HP_SYS_CLKRST;
                HP_SYS_CLKRST::regs().rmt_ctrl0().modify(|_, w| unsafe {
                    w.clk_div_num().bits(div);
                    w.clk_div_numerator().bits(0);
                    w.clk_div_denominator().bits(0)
                });
            }
            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)
                });
            }
            _ => {
                RMT::regs().sys_conf().modify(|_, w| unsafe {
                    w.sclk_div_num().bits(div);
                    w.sclk_div_a().bits(0);
                    w.sclk_div_b().bits(0)
                });
            }
        }
        RMT::regs()
            .sys_conf()
            .modify(|_, w| w.apb_fifo_mask().set_bit());
    }
    #[crate::handler]
    #[cfg_attr(place_rmt_driver_in_ram, ram)]
    pub(super) fn async_interrupt_handler() {
        let st = RMT::regs().int_st().read();
        for ch_idx in ChannelIndex::iter_all() {
            let raw_tx = unsafe { DynChannelAccess::<Tx>::conjure(ch_idx) };
            let raw_rx = unsafe { DynChannelAccess::<Rx>::conjure(ch_idx) };
            let ch_idx = ch_idx as u8;
            let channel = if st.ch_tx_end(ch_idx).bit() || st.ch_tx_err(ch_idx).bit() {
                raw_tx.unlisten_tx_interrupt(EnumSet::all());
                raw_tx.channel()
            } else if st.ch_tx_thr_event(ch_idx).bit() {
                // 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() {
                // RxFuture will enable the interrupt again if required.
                raw_rx.unlisten_rx_interrupt(Event::Threshold);
                raw_rx.channel()
            } else {
                continue;
            };
            WAKER[channel as usize].wake();
            return;
        }
    }
    impl<Dir: Direction> DynChannelAccess<Dir> {
        pub fn channel(self) -> u8 {
            if Dir::IS_TX {
                self.ch_idx as u8
            } else {
                self.ch_idx as u8 + ChannelIndex::MAX
            }
        }
        pub fn update(self) {
            let rmt = crate::peripherals::RMT::regs();
            let ch_idx = self.ch_idx as usize;
            if Dir::IS_TX {
                rmt.ch_tx_conf0(ch_idx)
                    .modify(|_, w| w.conf_update().set_bit());
            } else {
                rmt.ch_rx_conf1(ch_idx)
                    .modify(|_, w| w.conf_update().set_bit());
            }
            // The update needs to be synchronized into the SCLK domain before it takes effect.
            // Register writes or another update during that time may be lost, which breaks
            // transactions in unexpected ways when SCLK is slow.
            let delay_us = CONF_UPDATE_DELAY_US.load(Ordering::Relaxed);
2
            if delay_us > 0 {
                crate::rom::ets_delay_us(delay_us);
            }
        }
        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) });
            } else {
                rmt.ch_rx_conf0(ch_idx)
                    .modify(|_, w| unsafe { w.div_cnt().bits(divider) });
            }
        }
        pub fn memsize(self) -> MemSize {
            let rmt = RMT::regs();
            let ch_idx = self.ch_idx as usize;
            let blocks = if Dir::IS_TX {
                rmt.ch_tx_conf0(ch_idx).read().mem_size().bits()
            } else {
                rmt.ch_rx_conf0(ch_idx).read().mem_size().bits()
            };
            MemSize::from_blocks(blocks)
        }
        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) });
            } else {
                rmt.ch_rx_conf0(ch_idx)
                    .modify(|_, w| unsafe { w.mem_size().bits(blocks) });
            }
        }
        #[inline(always)]
        pub fn reset_channel_clock_divider(self) {
            #[cfg(esp32c5)]
            {
                let mask = 1u32 << self.channel();
                let rmt = RMT::regs();
                rmt.ref_cnt_rst().write(|w| unsafe { w.bits(mask) });
                rmt.ref_cnt_rst().write(|w| unsafe { w.bits(0) });
            }
        }
    }
    // documented in re-export below
    #[allow(missing_docs)]
    pub const MAX_TX_LOOPCOUNT: u16 =
        max_from_register_spec!(u16, ch_tx_lim, CH_TX_LIM_SPEC, TX_LOOP_NUM_W);
    impl DynChannelAccess<Tx> {
        #[inline(always)]
        pub fn set_loopmode(self, mode: Option<LoopMode>) {
            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());
                    #[cfg(rmt_has_tx_loop_auto_stop)]
                    w.loop_stop_en().bit(matches!(mode, LoopMode::Finite(_)));
                    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());
            } else {
                rmt.ch_tx_lim(ch_idx)
                    .modify(|_, w| w.tx_loop_cnt_en().clear_bit());
            }
        }
        #[inline(always)]
        pub fn clear_tx_interrupts(self, events: impl Into<EnumSet<Event>>) {
            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;
                    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
                },
            );
        }
        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(ch_idx)
                .modify(|_, w| w.tx_conti_mode().bit(continuous));
        }
        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(ch_idx)
                .modify(|_, w| w.mem_tx_wrap_en().bit(wrap));
        }
        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(ch_idx)
                .write(|w| unsafe { w.carrier_high().bits(high).carrier_low().bits(low) });
            rmt.ch_tx_conf0(ch_idx).modify(|_, w| {
                w.carrier_en().bit(carrier);
                w.carrier_eff_en().set_bit();
                w.carrier_out_lv().bit(level.into())
            });
        }
        pub fn tx_idle_output(self) -> (bool, Level) {
            let rmt = crate::peripherals::RMT::regs();
            let ch_idx = self.ch_idx as usize;
            let reg = rmt.ch_tx_conf0(ch_idx).read();
            (reg.idle_out_en().bit(), reg.idle_out_lv().bit().into())
        }
        pub fn set_tx_idle_output(self, enable: bool, level: Level) {
            let rmt = crate::peripherals::RMT::regs();
            let ch_idx = self.ch_idx as usize;
            rmt.ch_tx_conf0(ch_idx)
                .modify(|_, w| w.idle_out_en().bit(enable).idle_out_lv().bit(level.into()));
        }
        pub fn start_tx(self) {
            let rmt = crate::peripherals::RMT::regs();
            let ch_idx = self.ch_idx as usize;
            self.reset_channel_clock_divider();
            rmt.ch_tx_conf0(ch_idx).modify(|_, 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(always)]
        pub fn get_tx_status(self) -> Option<Event> {
            let rmt = crate::peripherals::RMT::regs();
            let reg = rmt.int_raw().read();
            let ch_idx = self.ch_idx as u8;
            if reg.ch_tx_end(ch_idx).bit() {
                Some(Event::End)
            } else if reg.ch_tx_err(ch_idx).bit() {
                Some(Event::Error)
            } else if reg.ch_tx_loop(ch_idx).bit() {
                Some(Event::LoopCount)
            } else if reg.ch_tx_thr_event(ch_idx).bit() {
                Some(Event::Threshold)
            } else {
                None
            }
        }
        pub fn set_tx_threshold(self, threshold: u8) {
            let rmt = crate::peripherals::RMT::regs();
            let ch_idx = self.ch_idx as usize;
            rmt.ch_tx_lim(ch_idx)
                .modify(|_, w| unsafe { w.tx_lim().bits(threshold as u16) });
        }
        pub fn is_tx_loopcount_interrupt_set(self) -> bool {
            let rmt = crate::peripherals::RMT::regs();
            let ch_idx = self.ch_idx as u8;
            rmt.int_raw().read().ch_tx_loop(ch_idx).bit()
        }
        // Requires an update() call
        pub fn stop_tx(self) {
            let rmt = crate::peripherals::RMT::regs();
            let ch_idx = self.ch_idx as usize;
            rmt.ch_tx_conf0(ch_idx).modify(|_, w| w.tx_stop().set_bit());
        }
        #[inline(always)]
        pub fn set_tx_interrupt(self, events: EnumSet<Event>, enable: bool) {
            let rmt = crate::peripherals::RMT::regs();
            RMT_LOCK.lock(|| {
                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)]
        pub fn hw_offset(self) -> usize {
            let rmt = crate::peripherals::RMT::regs();
            let ch_idx = self.ch_idx as usize;
            let offset = rmt.ch_tx_status(ch_idx).read().mem_raddr_ex().bits();
            usize::from(offset) - self.channel_ram_start_offset()
        }
    }
    // documented in re-export below
    #[allow(missing_docs)]
    pub const MAX_RX_IDLE_THRESHOLD: u16 = {
        // TODO: unify naming in PAC
        cfg_select! {
            any(esp32p4, esp32s31) => {
                // 15-bit IDLE_THRES in RX CONF0 (PAC aliases that register to TX CONF0).
                32767
            }
            _ => {
                max_from_register_spec!(u16, ch_rx_conf0, CH_RX_CONF0_SPEC, IDLE_THRES_W)
            }
        }
    };
    impl DynChannelAccess<Rx> {
        #[inline(always)]
        pub fn clear_rx_interrupts(self, events: impl Into<EnumSet<Event>>) {
            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;
                    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
                },
            );
        }
        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));
        }
        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_conf0(ch_idx).modify(|_, w| {
                w.carrier_en()
                    .bit(carrier)
                    .carrier_out_lv()
                    .bit(level.into())
            });
        }
        pub fn start_rx(self) {
            let rmt = crate::peripherals::RMT::regs();
            let ch_idx = self.ch_idx as u8;
            self.reset_channel_clock_divider();
            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.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()
            });
        }
        // Return the first flag that is set of, in order of decreasing priority,
        // Event::Error, Event::End, Event::Threshold
        #[inline(always)]
        pub fn get_rx_status(self) -> Option<Event> {
            let rmt = crate::peripherals::RMT::regs();
            let reg = rmt.int_raw().read();
            let ch_idx = self.ch_idx as u8;
2424937
            if reg.ch_rx_end(ch_idx).bit() {
                Some(Event::End)
2424937
            } else if reg.ch_rx_err(ch_idx).bit() {
                Some(Event::Error)
2424936
            } else if reg.ch_rx_thr_event(ch_idx).bit() {
                Some(Event::Threshold)
            } else {
                None
            }
        }
        pub fn set_rx_threshold(self, threshold: u16) {
            let rmt = crate::peripherals::RMT::regs();
2
            let ch_idx = self.ch_idx as usize;
            rmt.ch_rx_lim(ch_idx)
                .modify(|_, 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
        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());
        }
        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)
            });
        }
        pub fn set_rx_idle_threshold(self, value: u16) {
            debug_assert!(value <= MAX_RX_IDLE_THRESHOLD);
            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) });
        }
        #[inline(always)]
        pub fn set_rx_interrupt(self, events: EnumSet<Event>, enable: bool) {
            let rmt = crate::peripherals::RMT::regs();
            RMT_LOCK.lock(|| {
                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
                    },
                )
            });
        }
        pub fn hw_offset(self) -> usize {
            let rmt = crate::peripherals::RMT::regs();
            let ch_idx = self.ch_idx as usize;
            let offset = rmt.ch_rx_status(ch_idx).read().mem_waddr_ex().bits();
            usize::from(offset) - self.channel_ram_start_offset()
        }
    }
}
#[cfg(any(esp32, esp32s2))]
mod chip_specific {
    use enumset::EnumSet;
    #[cfg(place_rmt_driver_in_ram)]
    use procmacros::ram;
    use super::{
        ChannelIndex,
        ClockSource,
        ConfigError,
        Direction,
        DynChannelAccess,
        Event,
        Level,
        MemSize,
        RMT_LOCK,
        Rx,
        Tx,
        WAKER,
    };
    use crate::{peripherals::RMT, soc::clocks, time::Rate};
    pub(super) fn validate_clock(
        source: ClockSource,
        frequency: Rate,
    ) -> Result<(u8, Rate), ConfigError> {
        let source_frequency = Rate::from_hz(clocks::RmtInstance::sclk_source_frequency(source));
        if frequency != source_frequency {
            return Err(ConfigError::UnreachableTargetFrequency);
        }
        Ok((1, frequency))
    }
    pub(super) fn configure_clock(source: ClockSource, _div: u8, _frequency: Rate) {
        let rmt = RMT::regs();
        clocks::ClockTree::with(|clocks| {
            clocks::RmtInstance::Rmt.configure_sclk(clocks, source);
        });
        rmt.apb_conf().modify(|_, w| w.apb_fifo_mask().set_bit());
    }
    #[crate::handler]
    #[cfg_attr(place_rmt_driver_in_ram, ram)]
    pub(super) fn async_interrupt_handler() {
        let st = RMT::regs().int_st().read();
        for ch_idx in ChannelIndex::iter_all() {
            let raw_tx = unsafe { DynChannelAccess::<Tx>::conjure(ch_idx) };
            let raw_rx = unsafe { DynChannelAccess::<Rx>::conjure(ch_idx) };
            let ch_idx = ch_idx as u8;
            if st.ch_tx_end(ch_idx).bit() {
                raw_tx.unlisten_tx_interrupt(EnumSet::all());
            } else if st.ch_rx_end(ch_idx).bit() {
                raw_rx.unlisten_rx_interrupt(EnumSet::all());
            } else if st.ch_err(ch_idx).bit() {
                // On error interrupts, don't bother whether the channel is in Rx or Tx mode, just
                // unlisten all interrupts and wake.
                raw_tx.unlisten_tx_interrupt(EnumSet::all());
                raw_rx.unlisten_rx_interrupt(EnumSet::all());
            } else if st.ch_tx_thr_event(ch_idx).bit() {
                raw_tx.unlisten_tx_interrupt(Event::Threshold);
            } else {
                continue;
            }
            // raw_tx.channel() == raw_rx.channel() == ch_idx for these devices
            WAKER[ch_idx as usize].wake();
            return;
        }
    }
    impl<Dir: Direction> DynChannelAccess<Dir> {
        pub fn channel(self) -> u8 {
            self.ch_idx as u8
        }
        pub fn update(self) {
            // no-op
        }
        pub fn set_divider(self, divider: u8) {
            let rmt = crate::peripherals::RMT::regs();
            let ch = self.ch_idx as usize;
            rmt.chconf0(ch)
                .modify(|_, w| unsafe { w.div_cnt().bits(divider) });
        }
        pub fn memsize(self) -> MemSize {
            let rmt = crate::peripherals::RMT::regs();
            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) {
            let rmt = crate::peripherals::RMT::regs();
            let ch = self.ch_idx as usize;
            rmt.chconf0(ch)
                .modify(|_, w| unsafe { w.mem_size().bits(value.blocks()) });
        }
    }
    // documented in re-export below
    #[allow(missing_docs)]
    #[cfg(rmt_has_tx_loop_count)]
    pub const MAX_TX_LOOPCOUNT: u16 =
        max_from_register_spec!(u16, ch_tx_lim, CH_TX_LIM_SPEC, TX_LOOP_NUM_W);
    impl DynChannelAccess<Tx> {
        #[cfg(rmt_has_tx_loop_count)]
        #[inline(always)]
        pub fn set_loopmode(self, mode: Option<super::LoopMode>) {
            let rmt = crate::peripherals::RMT::regs();
            let ch = self.ch_idx as usize;
            if let Some(mode) = mode {
                rmt.ch_tx_lim(ch).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())
                });
                // FIXME: Is this required?
                rmt.ch_tx_lim(ch)
                    .modify(|_, w| w.loop_count_reset().clear_bit());
            } else {
                rmt.ch_tx_lim(ch)
                    .modify(|_, w| w.tx_loop_cnt_en().clear_bit());
            }
        }
        #[inline(always)]
        pub fn clear_tx_interrupts(self, events: impl Into<EnumSet<Event>>) {
            let rmt = crate::peripherals::RMT::regs();
            let events = events.into();
            rmt.int_clr().write(
                #[inline(always)]
                |w| {
                    let ch = self.ch_idx as u8;
                    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
                },
            );
        }
        pub fn set_tx_continuous(self, continuous: bool) {
            let rmt = crate::peripherals::RMT::regs();
            let ch = self.ch_idx as usize;
            rmt.chconf1(ch)
                .modify(|_, w| w.tx_conti_mode().bit(continuous));
        }
        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));
        }
        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.chconf0(ch).modify(|_, w| {
                w.carrier_en()
                    .bit(carrier)
                    .carrier_out_lv()
                    .bit(level.into())
            });
        }
        pub fn tx_idle_output(self) -> (bool, Level) {
            let rmt = crate::peripherals::RMT::regs();
            let ch = self.ch_idx as usize;
            let reg = rmt.chconf1(ch).read();
            (reg.idle_out_en().bit(), reg.idle_out_lv().bit().into())
        }
        pub fn set_tx_idle_output(self, enable: bool, level: Level) {
            let rmt = crate::peripherals::RMT::regs();
            let ch = self.ch_idx as usize;
            rmt.chconf1(ch)
                .modify(|_, w| w.idle_out_en().bit(enable).idle_out_lv().bit(level.into()));
        }
        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 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()
            });
        }
        // Return the first flag that is set of, in order of decreasing priority,
        // Event::Error, Event::End, Event::LoopCount, Event::Threshold
        #[inline(always)]
        pub fn get_tx_status(self) -> Option<Event> {
            let rmt = crate::peripherals::RMT::regs();
            let reg = rmt.int_raw().read();
            let ch = self.ch_idx as u8;
            if reg.ch_tx_end(ch).bit() {
                return Some(Event::End);
            }
            if reg.ch_err(ch).bit() {
                return Some(Event::Error);
            }
            #[cfg(rmt_has_tx_loop_count)]
            if reg.ch_tx_loop(ch).bit() {
                return Some(Event::LoopCount);
            }
            if reg.ch_tx_thr_event(ch).bit() {
                return Some(Event::Threshold);
            }
            None
        }
        pub fn set_tx_threshold(self, threshold: u8) {
            let rmt = crate::peripherals::RMT::regs();
            let ch = self.ch_idx as usize;
            rmt.ch_tx_lim(ch)
                .modify(|_, w| unsafe { w.tx_lim().bits(threshold as u16) });
        }
        #[cfg(rmt_has_tx_loop_count)]
        pub fn is_tx_loopcount_interrupt_set(self) -> bool {
            let rmt = crate::peripherals::RMT::regs();
            let ch = self.ch_idx as u8;
            rmt.int_raw().read().ch_tx_loop(ch).bit()
        }
        // It would be better to simply not define this method if not supported, but that would
        // make the implementation of ContinuousTxTransaction::stop_impl much more awkward.
        #[cfg(not(rmt_has_tx_loop_count))]
        #[allow(unused)]
        pub fn is_tx_loopcount_interrupt_set(self) -> bool {
            false
        }
        #[cfg(rmt_has_tx_immediate_stop)]
        pub fn stop_tx(self) {
            let rmt = crate::peripherals::RMT::regs();
            let ch = self.ch_idx as usize;
            rmt.chconf1(ch).modify(|_, w| w.tx_stop().set_bit());
        }
        #[cfg(not(rmt_has_tx_immediate_stop))]
        pub fn stop_tx(self) {
            let ptr = self.channel_ram_start();
            for idx in 0..self.memsize().codes() {
                unsafe {
                    ptr.add(idx).write_volatile(super::PulseCode::end_marker());
                }
            }
        }
        #[inline(always)]
        pub fn set_tx_interrupt(self, events: EnumSet<Event>, enable: bool) {
            let rmt = crate::peripherals::RMT::regs();
            RMT_LOCK.lock(|| {
                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)]
        pub fn hw_offset(self) -> usize {
            let rmt = crate::peripherals::RMT::regs();
            let ch = self.ch_idx as usize;
            let offset = rmt.chstatus(ch).read().mem_raddr_ex().bits();
            usize::from(offset) - self.channel_ram_start_offset()
        }
    }
    // documented in re-export below
    #[allow(missing_docs)]
    pub const MAX_RX_IDLE_THRESHOLD: u16 =
        max_from_register_spec!(u16, chconf0, CHCONF0_SPEC, IDLE_THRES_W);
    impl DynChannelAccess<Rx> {
        #[inline(always)]
        pub fn clear_rx_interrupts(self, events: impl Into<EnumSet<Event>>) {
            let rmt = crate::peripherals::RMT::regs();
            let events = events.into();
            rmt.int_clr().write(
                #[inline(always)]
                |w| {
                    let ch = self.ch_idx as u8;
                    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) {
            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.chconf0(ch).modify(|_, w| {
                w.carrier_en()
                    .bit(carrier)
                    .carrier_out_lv()
                    .bit(level.into())
            });
        }
        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 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()
            });
        }
        // Return the first flag that is set of, in order of decreasing priority,
        // Event::Error, Event::End, Event::Threshold
        #[inline(always)]
        pub fn get_rx_status(self) -> Option<Event> {
            let rmt = crate::peripherals::RMT::regs();
            let reg = rmt.int_raw().read();
            let ch = self.ch_idx as u8;
            if reg.ch_rx_end(ch).bit() {
                Some(Event::End)
            } else if reg.ch_err(ch).bit() {
                Some(Event::Error)
            } else {
                None
            }
        }
        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
            // data ended (buffer end or idle threshold). Depending on the current channel
            // settings, this might take a looong time.
            //
            // However, we do need to reliably stop the receiver on `Channel` drop to avoid
            // subsequent transactions receiving some initial garbage.
            //
            // This code attempts to work around this limitation by
            //
            // 1) setting the mem_owner to an invalid value. This doesn't seem to trigger an error
            //    immediately, so presumably the error only occurs when a new pulse code would be
            //    written.
            // 2) lowering the idle threshold and change other settings to make exceeding the
            //    threshold more likely (lower clock divider, enable and set an input filter that is
            //    longer than the idle threshold). The latter should have the same effect as
            //    reconnecting the pin to a constant level, but that tricky to do since we'd also
            //    need restore it afterwards.
            //
            // On its own, 2) should be sufficient and quickly result in an `End` condition. If
            // this is overlooking anything and a new code does happen to be received, 1) should
            // ensure that an `Error` condition occurs and in any case, we never block here for
            // long.
            if force {
                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(ch).from_modify(|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(ch).from_modify(|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(ch).modify(|_, w| unsafe {
                    w.idle_thres().bits(old_idle_thres);
                    w.div_cnt().bits(old_div)
                });
                rmt.chconf1(ch).modify(|_, 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(ch).modify(|_, w| w.rx_en().clear_bit());
            }
        }
        pub fn set_rx_filter_threshold(self, value: u8) {
            let rmt = crate::peripherals::RMT::regs();
            let ch = self.ch_idx as usize;
            rmt.chconf1(ch).modify(|_, w| unsafe {
                w.rx_filter_en().bit(value > 0);
                w.rx_filter_thres().bits(value)
            });
        }
        pub fn set_rx_idle_threshold(self, value: u16) {
            #[allow(clippy::absurd_extreme_comparisons)]
            {
                debug_assert!(value <= MAX_RX_IDLE_THRESHOLD);
            }
            let rmt = crate::peripherals::RMT::regs();
            let ch = self.ch_idx as usize;
            rmt.chconf0(ch)
                .modify(|_, w| unsafe { w.idle_thres().bits(value) });
        }
        #[inline(always)]
        pub fn set_rx_interrupt(self, events: EnumSet<Event>, enable: bool) {
            let rmt = crate::peripherals::RMT::regs();
            RMT_LOCK.lock(|| {
                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
                    },
                )
            });
        }
        pub fn hw_offset(self) -> usize {
            let rmt = crate::peripherals::RMT::regs();
            let ch = self.ch_idx as usize;
            let offset = rmt.chstatus(ch).read().mem_waddr_ex().bits();
            usize::from(offset) - self.channel_ram_start_offset()
        }
    }
}
/// The largest valid value for [`RxChannelConfig::with_idle_threshold`].
pub use chip_specific::MAX_RX_IDLE_THRESHOLD;
/// The largest valid value for loopcounts in [`LoopMode`].
#[cfg(rmt_has_tx_loop_count)]
pub use chip_specific::MAX_TX_LOOPCOUNT;