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}