Skip to main content

esp_idf_hal/rmt/
encoder.rs

1mod copy_encoder;
2pub use copy_encoder::*;
3mod bytes_encoder;
4pub use bytes_encoder::*;
5
6#[cfg(esp_idf_version_at_least_5_3_0)]
7#[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_3_0)))]
8pub mod simple_encoder;
9
10use alloc::boxed::Box;
11use core::mem;
12
13use esp_idf_sys::*;
14
15/// This trait represents an RMT encoder that is used to encode data for transmission.
16pub trait RawEncoder {
17    /// The type of input data the encoder can encode.
18    type Item;
19
20    /// Returns a mutable reference to the underlying `rmt_encoder_t`.
21    ///
22    /// The functions of the `rmt_encoder_t` will be called by the RMT driver.
23    fn handle(&mut self) -> &mut rmt_encoder_t;
24
25    /// Reset the encoder state.
26    ///
27    /// # Errors
28    ///
29    /// If resetting the encoder fails, it should return an [`EspError`]
30    /// with the code [`ESP_FAIL`].
31    fn reset(&mut self) -> Result<(), EspError> {
32        esp!(unsafe { rmt_encoder_reset(self.handle()) })
33    }
34}
35
36impl<E: RawEncoder> RawEncoder for &mut E {
37    type Item = E::Item;
38
39    fn handle(&mut self) -> &mut rmt_encoder_t {
40        (*self).handle()
41    }
42
43    fn reset(&mut self) -> Result<(), EspError> {
44        (*self).reset()
45    }
46}
47
48/// RMT encoding state
49#[derive(Debug, Clone)]
50#[non_exhaustive]
51pub enum EncoderState {
52    /// The encoding session is in reset state
53    EncodingReset,
54    /// The encoding session is finished, the caller can continue with subsequent encoding
55    EncodingComplete,
56    /// The encoding artifact memory is full, the caller should return from current encoding session.
57    EncodingMemoryFull,
58    /// The encoding session has inserted the EOF marker to the symbol stream
59    #[cfg(esp_idf_version_at_least_5_5_0)]
60    #[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_5_0)))]
61    EncodingWithEof,
62}
63
64impl From<EncoderState> for rmt_encode_state_t {
65    fn from(value: EncoderState) -> Self {
66        match value {
67            EncoderState::EncodingReset => rmt_encode_state_t_RMT_ENCODING_RESET,
68            EncoderState::EncodingComplete => rmt_encode_state_t_RMT_ENCODING_COMPLETE,
69            EncoderState::EncodingMemoryFull => rmt_encode_state_t_RMT_ENCODING_MEM_FULL,
70            #[cfg(esp_idf_version_at_least_5_5_0)]
71            EncoderState::EncodingWithEof => rmt_encode_state_t_RMT_ENCODING_WITH_EOF,
72        }
73    }
74}
75
76impl From<rmt_encode_state_t> for EncoderState {
77    fn from(value: rmt_encode_state_t) -> Self {
78        #[allow(non_upper_case_globals)]
79        match value {
80            rmt_encode_state_t_RMT_ENCODING_RESET => Self::EncodingReset,
81            rmt_encode_state_t_RMT_ENCODING_COMPLETE => Self::EncodingComplete,
82            rmt_encode_state_t_RMT_ENCODING_MEM_FULL => Self::EncodingMemoryFull,
83            #[cfg(esp_idf_version_at_least_5_5_0)]
84            rmt_encode_state_t_RMT_ENCODING_WITH_EOF => Self::EncodingWithEof,
85            _ => panic!("Unknown rmt_encode_state_t value: {value}"),
86        }
87    }
88}
89
90/// A handle to an RMT channel.
91///
92/// For the special encoders like [`CopyEncoder`] or [`BytesEncoder`] that
93/// directly write to the RMT memory, this handle is used to access the channel.
94///
95/// For third-party encoders this functionality is not exposed. To prevent misuse,
96/// this type wraps the raw handle and does not expose it.
97#[derive(Debug)]
98pub struct RmtChannelHandle {
99    handle: rmt_channel_handle_t,
100}
101
102impl RmtChannelHandle {
103    /// Returns the underlying `rmt_channel_handle_t`.
104    ///
105    /// Note that this does not guarantee that the handle is still valid.
106    #[must_use]
107    pub(crate) fn as_ptr(&self) -> rmt_channel_handle_t {
108        // This is explicitly not public to prevent misuse.
109        //
110        // Calling any of the ESP-IDF functions will likely violate invariants
111        // of `TxChannelDriver` or `RxChannelDriver`.
112        // The handle is only used by the encoders that copy the data directly
113        // into the RMT memory (e.g. `CopyEncoder` or `BytesEncoder`).
114        self.handle
115    }
116}
117
118/// A trait for implementing custom RMT encoders in rust.
119///
120/// An RMT encoder is part of the RMT TX transaction, whose responsibility
121/// is to generate and write the correct RMT symbols into hardware memory
122/// or DMA buffer at a specific time.
123///
124/// There are some special restrictions for an encoding function:
125/// - During a single transaction, the encoding function may be called
126///   multiple times. This is necessary because the target RMT memory
127///   block cannot hold all the artifacts at once. To overcome this
128///   limitation, the driver utilizes a ping-pong approach, where the
129///   encoding session is divided into multiple parts. This means that
130///   the encoder needs to keep track of its state to continue encoding
131///   from where it left off in the previous part.
132/// - The encoding function is running in the ISR context.
133///   To speed up the encoding session, it is highly recommended to put
134///   the encoding function into IRAM. This can also avoid the cache miss during encoding.
135pub trait Encoder {
136    /// The type of input data the encoder can encode.
137    type Item;
138
139    /// Encode the user data into RMT symbols and write into RMT memory.
140    ///
141    /// This function might be called multiple times within a single transaction.
142    /// The encode function should return the state of the current encoding session.
143    ///
144    /// The supported states are listed in [`EncoderState`]. If the result contains
145    /// [`EncoderState::EncodingComplete`], it means the current encoder has finished
146    /// work.
147    ///
148    /// If the result contains [`EncoderState::EncodingMemoryFull`], the program needs
149    /// to yield from the current session, as there is no space to save more encoding
150    /// artifacts.
151    ///
152    /// # Note
153    ///
154    /// It is recommended to put this function implementation in the IRAM, to achieve a high
155    /// performance and less interrupt latency.
156    ///
157    /// # ISR Safety
158    ///
159    /// The encoding function will also be called from an ISR context, thus the function must not
160    /// call any blocking API.
161    fn encode(
162        &mut self,
163        handle: &mut RmtChannelHandle,
164        primary_data: &[Self::Item],
165    ) -> (usize, EncoderState);
166
167    /// Reset encoding state.
168    ///
169    /// # Errors
170    ///
171    /// With `ESP_FAIL` when it fails to reset the encoder.
172    fn reset(&mut self) -> Result<(), EspError>;
173}
174
175impl<E: RawEncoder> Encoder for E {
176    type Item = E::Item;
177
178    fn encode(
179        &mut self,
180        handle: &mut RmtChannelHandle,
181        primary_data: &[Self::Item],
182    ) -> (usize, EncoderState) {
183        let encoder_handle = self.handle();
184        let Some(encode) = encoder_handle.encode else {
185            return (0, EncoderState::EncodingReset);
186        };
187
188        let mut ret_state: rmt_encode_state_t = rmt_encode_state_t_RMT_ENCODING_RESET;
189        let data_size = mem::size_of_val::<[Self::Item]>(primary_data);
190
191        let written = unsafe {
192            encode(
193                encoder_handle,
194                handle.as_ptr(),
195                primary_data.as_ptr() as *const core::ffi::c_void,
196                data_size,
197                &raw mut ret_state,
198            )
199        };
200
201        (written, ret_state.into())
202    }
203
204    fn reset(&mut self) -> Result<(), EspError> {
205        let handle = self.handle();
206        if let Some(reset) = handle.reset {
207            esp!(unsafe { reset(handle) })?;
208        }
209
210        Ok(())
211    }
212}
213
214/// A helper function to convert a custom RMT encoder into a raw encoder
215/// that can be used by the RMT driver.
216///
217/// # Panics
218///
219/// If the allocation for the encoder wrapper fails.
220#[must_use]
221pub fn into_raw<E: Encoder>(encoder: E) -> EncoderWrapper<E> {
222    EncoderWrapper::new(encoder).expect("Failed to allocate memory for RMT encoder")
223}
224
225#[derive(Debug)]
226struct InternalEncoderWrapper<E> {
227    base: rmt_encoder_t, // the base "class", declares the standard encoder interface
228    encoder: Option<E>,
229}
230
231/// A type implementing [`Encoder`] can not be directly used by the driver,
232/// because the driver expects a different interface (see [`RawEncoder`]).
233///
234/// This type adapts an [`Encoder`] to a [`RawEncoder`], taking care of the
235/// unsafe parts.
236#[derive(Debug)]
237#[repr(C)]
238pub struct EncoderWrapper<E>(Box<InternalEncoderWrapper<E>>);
239
240impl<E: Encoder> EncoderWrapper<E> {
241    pub fn new(encoder: E) -> Result<Self, EspError> {
242        Ok(Self(Box::new(InternalEncoderWrapper::new(encoder))))
243    }
244}
245
246impl<E: Encoder> InternalEncoderWrapper<E> {
247    /// This function returns a pointer to self from a pointer to its field `base`.
248    ///
249    /// One might assume that this is redundant, because the `base` field is the first field in the struct
250    /// -> the pointer would point to the same address as the struct itself.
251    /// but this is not guaranteed by rust.
252    ///
253    /// In the C representation, it might add padding before the fields to align them.
254    /// In the rust representation, it doesn't even guarantee that the field is at
255    /// the start of the struct.
256    ///
257    /// See <https://doc.rust-lang.org/reference/type-layout.html>
258    unsafe fn containerof(ptr: *mut rmt_encoder_t) -> *mut Self {
259        // The given pointer points to the base field of this struct,
260        // in the C-Code they use the __containerof macro to get the pointer to the whole struct
261        //
262        // The below does the same.
263        //
264        // SAFETY: This struct is #[repr(C)]
265        ptr.cast::<u8>()
266            .sub(mem::offset_of!(Self, base))
267            .cast::<Self>()
268    }
269
270    pub fn new(encoder: E) -> Self {
271        Self {
272            base: rmt_encoder_t {
273                encode: Some(Self::encode),
274                reset: Some(Self::reset),
275                del: Some(Self::del),
276            },
277            encoder: Some(encoder),
278        }
279    }
280
281    fn encoder(&mut self) -> &mut E {
282        unsafe { self.encoder.as_mut().unwrap_unchecked() }
283    }
284
285    pub unsafe extern "C" fn encode(
286        encoder: *mut rmt_encoder_t,
287        tx_channel: rmt_channel_handle_t,
288        primary_data: *const ::core::ffi::c_void,
289        data_size: usize,
290        ret_state: *mut rmt_encode_state_t,
291    ) -> usize {
292        let this = Self::containerof(encoder).as_mut().unwrap();
293
294        let primary_data = core::slice::from_raw_parts(
295            primary_data.cast::<E::Item>(),
296            data_size / mem::size_of::<E::Item>(),
297        );
298
299        let mut channel_handle = RmtChannelHandle { handle: tx_channel };
300
301        let (written, state) = this.encoder().encode(&mut channel_handle, primary_data);
302
303        *ret_state = state.into();
304
305        written
306    }
307
308    pub unsafe extern "C" fn reset(encoder: *mut rmt_encoder_t) -> esp_err_t {
309        let this = Self::containerof(encoder).as_mut().unwrap();
310
311        match this.encoder().reset() {
312            Ok(()) => ESP_OK,
313            Err(_) => ESP_FAIL,
314        }
315    }
316
317    pub unsafe extern "C" fn del(encoder: *mut rmt_encoder_t) -> esp_err_t {
318        let this = Self::containerof(encoder).as_mut().unwrap();
319
320        // drop the encoder
321        this.encoder.take();
322
323        ESP_OK
324    }
325}
326
327impl<E: Encoder> RawEncoder for EncoderWrapper<E> {
328    type Item = E::Item;
329
330    fn handle(&mut self) -> &mut rmt_encoder_t {
331        &mut self.0.as_mut().base
332    }
333}