Skip to main content

esp_idf_hal/rmt/encoder/
simple_encoder.rs

1//! The [`SimpleEncoder`] provides a simpler interface than the [`Encoder`](crate::rmt::encoder::Encoder)
2//! to implement a custom RMT encoder.
3//!
4//! The encoder  will take care of allocating a target buffer for the output symbols
5//! and will call a user-provided callback ([`EncoderCallback`]) to fill the buffer with symbols.
6
7use core::ffi::c_void;
8use core::{mem, ptr, slice};
9
10use alloc::boxed::Box;
11
12use esp_idf_sys::*;
13
14use crate::rmt::encoder::RawEncoder;
15use crate::rmt::Symbol;
16
17/// The encoder was unable to encode all the input data into the provided buffer,
18/// it might have encoded some data (or it didn't), but there remains more to encode.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct NotEnoughSpace;
21
22/// Configuration for the [`SimpleEncoder`].
23#[derive(Debug, Clone)]
24pub struct SimpleEncoderConfig {
25    /// Minimum amount of free space, in RMT symbols, the encoder needs in order
26    /// to guarantee it always returns non-zero. Defaults to 64 if zero / not given.
27    pub min_chunk_size: usize,
28    // This field is intentionally hidden to prevent non-exhaustive pattern matching.
29    // You should only construct this struct using the `..Default::default()` pattern.
30    // If you use this field directly, your code might break in future versions.
31    #[doc(hidden)]
32    #[allow(dead_code)]
33    pub __internal: (),
34}
35
36impl Default for SimpleEncoderConfig {
37    fn default() -> Self {
38        Self {
39            min_chunk_size: 64,
40            __internal: (),
41        }
42    }
43}
44
45/// A helper to write symbols into a buffer, tracking how many symbols were written.
46#[derive(Debug)]
47pub struct SymbolBuffer<'a> {
48    symbols: &'a mut [Symbol],
49    written: usize,
50}
51
52impl<'a> SymbolBuffer<'a> {
53    /// Constructs a `SymbolBuffer` from its raw parts.
54    ///
55    /// # Safety
56    ///
57    /// You must ensure that the provided pointer is valid for both reads and writes, not null,
58    /// and properly aligned to construct a slice from it.
59    ///
60    /// The `written` parameter must not exceed the length of the slice.
61    /// The `free` parameter must be such that `written + free` does not exceed the length of the
62    /// slice.
63    #[must_use]
64    pub unsafe fn from_raw_parts(
65        symbols: *mut rmt_symbol_word_t,
66        written: usize,
67        free: usize,
68    ) -> Self {
69        let symbols = slice::from_raw_parts_mut(symbols as *mut Symbol, written + free);
70        Self { symbols, written }
71    }
72
73    /// Returns how many symbols have been written so far.
74    ///
75    /// This can also be interpreted as the position of the next
76    /// element to write into the buffer.
77    ///
78    /// Initially this will be `0`. If you write `n` symbols in the
79    /// callback and then return, the next time the callback is called,
80    /// the position will always be `n`.
81    /// If you then encode `m` more symbols, the next callback will always
82    /// be called with position `n + m`, and so on.
83    ///
84    /// In other words, the position is preserved across callback invocations.
85    ///
86    /// The only exception is when the encoder is reset,
87    /// (e.g. to beging a new transaction) in which case the position
88    /// will always restart at `0`.
89    #[must_use]
90    pub const fn position(&self) -> usize {
91        self.written
92    }
93
94    /// Returns how many more symbols can be written to the buffer.
95    #[must_use]
96    pub fn remaining(&self) -> usize {
97        self.symbols.len() - self.written
98    }
99
100    /// Tries to write all symbols to the buffer.
101    ///
102    /// # Errors
103    ///
104    /// If there is not enough space, it will not write anything and return [`NotEnoughSpace`]
105    /// to indicate that the buffer is too small.
106    pub fn write_all(&mut self, symbols: &[Symbol]) -> Result<(), NotEnoughSpace> {
107        if self.remaining() < symbols.len() {
108            return Err(NotEnoughSpace);
109        }
110
111        for &symbol in symbols {
112            self.symbols[self.written] = symbol;
113            self.written += 1;
114        }
115
116        Ok(())
117    }
118
119    /// Returns the underlying slice of symbols that have been written so far.
120    #[must_use]
121    pub fn as_mut_slice(&mut self) -> &mut [Symbol] {
122        &mut self.symbols[..self.written]
123    }
124}
125
126/// This function implements the C API that the simple encoder expects for the callback
127/// and delegates the call to the rust type `S` which implements `EncoderCallback`.
128unsafe extern "C" fn delegator<S: EncoderCallback>(
129    data: *const c_void,
130    data_size: usize,
131    symbols_written: usize,
132    symbols_free: usize,
133    symbols: *mut rmt_symbol_word_t,
134    done: *mut bool,
135    arg: *mut c_void,
136) -> usize {
137    let callback = &mut *(arg as *mut S);
138    // The size of the data is in bytes -> must be converted to number of T items.
139    let data_slice = slice::from_raw_parts(
140        data as *const S::Item,
141        data_size / mem::size_of::<S::Item>(),
142    );
143    let mut buffer = SymbolBuffer::from_raw_parts(symbols, symbols_written, symbols_free);
144
145    // The function returns the following status:
146    // 1. `0` if the given buffer for writing symbols is too small,
147    //    like it needs 8 symbols to encode one data entry, but a buffer
148    //    with only 4 symbols is given.
149    // 2. a value `n > 0` indicating that `n` symbols were written in this callback round.
150    //
151    // If it finishes encoding all data, it sets `*done = true`.
152    // This can happen with status 1 or 2 (you don't have to write symbols to indicate that you are done).
153    //
154    // The number of written symbols is tracked by the `SymbolBuffer`.
155    //
156    // Important: It counts the output symbols, not the input data items!
157    //            If one processes a byte as an input into 8 symbols, then
158    //            the function should return `8`.
159    if let Ok(()) = callback.encode(data_slice, &mut buffer) {
160        *done = true;
161    }
162
163    // Calculate how many symbols were written in this call:
164    buffer.position() - symbols_written
165}
166
167/// Trait that is implemented by types that can be used as callbacks for the [`SimpleEncoder`].
168pub trait EncoderCallback {
169    /// The type of input data that the encoder can encode.
170    type Item;
171
172    /// This function encodes the provided input data into RMT symbols and writes them into the provided
173    /// `SymbolBuffer`.
174    ///
175    /// To do this, a buffer is allocated in which the resulting symbols can be written. It might happen that there
176    /// is not enough space in the buffer to encode all input data. In this case, the function has two options:
177    /// 1. It can immediately return [`NotEnoughSpace`] to indicate that the buffer is too small.
178    ///    The function will later be called again with a larger buffer. You should eventually process the data,
179    ///    and not return [`NotEnoughSpace`] forever.
180    ///
181    /// 2. It can start encoding the input data, and write symbols into the buffer until it runs out of space.
182    ///    It should return with [`NotEnoughSpace`]. The function will later be called again with more
183    ///    space, making it possible to continue encoding the remaining input data.
184    ///
185    /// The function takes a slice of input data of your chosen type [`EncoderCallback::Item`], this is the same data
186    /// that is passed to the RMT driver when sending data. The slice will not change between unfinished calls.
187    ///
188    /// For example, if you start processing an `input_data` of 10 elements, and you return with
189    /// [`NotEnoughSpace`] after encoding 4 elements, the next time the function is called,
190    /// the `input_data` will still be the same 10 elements.
191    ///
192    /// It is your responsibility to keep track of how many input elements you have already processed.
193    /// If the number of output symbols are a multiple of the number of input elements, you can use [`SymbolBuffer::position`]
194    /// to track how many input elements have been processed so far:
195    /// 1 input element = 8 output symbols -> position / 8 = number of input elements processed.
196    ///
197    /// Once you have processed all input elements, you should return with [`Ok`].
198    ///
199    /// # ISR Safety
200    ///
201    /// This function is called from an ISR context. Care should be taken not to call std,
202    /// libc or FreeRTOS APIs (except for a few allowed ones).
203    ///
204    /// You are not allowed to block, but you are allowed to call FreeRTOS APIs with the FromISR suffix.
205    fn encode(
206        &mut self,
207        input_data: &[Self::Item],
208        buffer: &mut SymbolBuffer<'_>,
209    ) -> Result<(), NotEnoughSpace>;
210}
211
212/// This type represents the simple encoder, it wraps a [`EncoderCallback`] to delegate
213/// the encoding work to it.
214#[derive(Debug)]
215pub struct SimpleEncoder<T> {
216    _encoder: Box<T>,
217    handle: rmt_encoder_handle_t,
218}
219
220impl<T: EncoderCallback> SimpleEncoder<T> {
221    /// Constructs a new simple encoder with the given callback and configuration.
222    pub fn with_config(encoder: T, config: &SimpleEncoderConfig) -> Result<Self, EspError> {
223        let mut encoder = Box::new(encoder);
224
225        // SAFETY: The reference will only be used to mutate the encoder and never to move it.
226        let reference = encoder.as_mut();
227        let sys_config = rmt_simple_encoder_config_t {
228            callback: Some(delegator::<T>),
229            arg: reference as *mut T as *mut c_void,
230            min_chunk_size: config.min_chunk_size,
231        };
232
233        let mut handle: rmt_encoder_handle_t = ptr::null_mut();
234        // SAFETY: the config is copied by the c code
235        esp!(unsafe { rmt_new_simple_encoder(&sys_config, &mut handle) })?;
236        Ok(Self {
237            handle,
238            _encoder: encoder,
239        })
240    }
241}
242
243impl<T: EncoderCallback> RawEncoder for SimpleEncoder<T> {
244    type Item = T::Item;
245
246    fn handle(&mut self) -> &mut rmt_encoder_t {
247        unsafe { &mut *self.handle }
248    }
249}
250
251impl<T> Drop for SimpleEncoder<T> {
252    fn drop(&mut self) {
253        // This is calling encoder->del(encoder);
254        unsafe { rmt_del_encoder(self.handle) };
255    }
256}