ws2812_esp32_rmt_driver/driver/
esp32_rmt.rs

1#![cfg_attr(not(target_vendor = "espressif"), allow(dead_code))]
2
3use core::convert::From;
4use core::error::Error;
5use core::fmt;
6use core::time::Duration;
7
8#[cfg(not(target_vendor = "espressif"))]
9use core::marker::PhantomData;
10
11#[cfg(not(target_vendor = "espressif"))]
12use crate::mock::esp_idf_hal;
13#[cfg(target_vendor = "espressif")]
14use esp_idf_hal::rmt::{PinState, Pulse, Symbol};
15use esp_idf_hal::{
16    gpio::OutputPin,
17    peripheral::Peripheral,
18    rmt::{config::TransmitConfig, RmtChannel, TxRmtDriver},
19    units::Hertz,
20};
21
22#[cfg(not(target_vendor = "espressif"))]
23use crate::mock::esp_idf_sys;
24use esp_idf_sys::EspError;
25
26/// T0H duration time (0 code, high voltage time)
27const WS2812_T0H_NS: Duration = Duration::from_nanos(400);
28/// T0L duration time (0 code, low voltage time)
29const WS2812_T0L_NS: Duration = Duration::from_nanos(850);
30/// T1H duration time (1 code, high voltage time)
31const WS2812_T1H_NS: Duration = Duration::from_nanos(800);
32/// T1L duration time (1 code, low voltage time)
33const WS2812_T1L_NS: Duration = Duration::from_nanos(450);
34
35/// Converter to a sequence of RMT items.
36#[repr(C)]
37struct Ws2812Esp32RmtItemEncoder {
38    /// The RMT item that represents a 0 code.
39    #[cfg(target_vendor = "espressif")]
40    bit0: Symbol,
41    /// The RMT item that represents a 1 code.
42    #[cfg(target_vendor = "espressif")]
43    bit1: Symbol,
44}
45
46impl Ws2812Esp32RmtItemEncoder {
47    /// Creates a new `Ws2812Esp32RmtItemEncoder`.
48    fn new(
49        clock_hz: Hertz,
50        t0h: &Duration,
51        t0l: &Duration,
52        t1h: &Duration,
53        t1l: &Duration,
54    ) -> Result<Self, Ws2812Esp32RmtDriverError> {
55        #[cfg(target_vendor = "espressif")]
56        {
57            let (bit0, bit1) = (
58                Symbol::new(
59                    Pulse::new_with_duration(clock_hz, PinState::High, &t0h)?,
60                    Pulse::new_with_duration(clock_hz, PinState::Low, &t0l)?,
61                ),
62                Symbol::new(
63                    Pulse::new_with_duration(clock_hz, PinState::High, &t1h)?,
64                    Pulse::new_with_duration(clock_hz, PinState::Low, &t1l)?,
65                ),
66            );
67            Ok(Self { bit0, bit1 })
68        }
69        #[cfg(not(target_vendor = "espressif"))]
70        {
71            let _ = (clock_hz, t0h, t0l, t1h, t1l);
72            Ok(Self {})
73        }
74    }
75    /// Encodes a block of data as a sequence of RMT items.
76    ///
77    /// # Arguments
78    ///
79    /// * `src` - The block of data to encode.
80    ///
81    /// # Returns
82    ///
83    /// An iterator over the RMT items that represent the encoded data.
84    #[inline]
85    #[cfg(target_vendor = "espressif")]
86    fn encode_iter<'a, 'b, T>(&'a self, src: T) -> impl Iterator<Item = Symbol> + Send + 'a
87    where
88        'b: 'a,
89        T: Iterator<Item = u8> + Send + 'b,
90    {
91        src.flat_map(move |v| {
92            (0..(u8::BITS as usize)).map(move |i| {
93                if v & (1 << (7 - i)) != 0 {
94                    self.bit1
95                } else {
96                    self.bit0
97                }
98            })
99        })
100    }
101}
102
103/// WS2812 ESP32 RMT Driver error.
104#[derive(Debug)]
105#[repr(transparent)]
106pub struct Ws2812Esp32RmtDriverError {
107    source: EspError,
108}
109
110#[cfg(not(feature = "std"))]
111impl Ws2812Esp32RmtDriverError {
112    /// The `EspError` source of this error, if any.
113    ///
114    /// This is a workaround function until `core::error::Error` added to `esp_sys::EspError`.
115    pub fn source(&self) -> Option<&EspError> {
116        Some(&self.source)
117    }
118}
119
120impl Error for Ws2812Esp32RmtDriverError {
121    fn source(&self) -> Option<&(dyn Error + 'static)> {
122        #[cfg(feature = "std")]
123        {
124            Some(&self.source)
125        }
126        #[cfg(not(feature = "std"))]
127        {
128            None
129        }
130    }
131}
132
133impl fmt::Display for Ws2812Esp32RmtDriverError {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        self.source.fmt(f)
136    }
137}
138
139impl From<EspError> for Ws2812Esp32RmtDriverError {
140    fn from(source: EspError) -> Self {
141        Self { source }
142    }
143}
144
145/// Builder for `Ws2812Esp32RmtDriver`.
146///
147/// # Examples
148///
149///
150/// ```
151/// # #[cfg(not(target_vendor = "espressif"))]
152/// # use ws2812_esp32_rmt_driver::mock::esp_idf_hal;
153/// #
154/// # use core::time::Duration;
155/// # use esp_idf_hal::peripherals::Peripherals;
156/// # use esp_idf_hal::rmt::config::TransmitConfig;
157/// # use esp_idf_hal::rmt::TxRmtDriver;
158/// # use ws2812_esp32_rmt_driver::driver::Ws2812Esp32RmtDriverBuilder;
159/// #
160/// # let peripherals = Peripherals::take().unwrap();
161/// # let led_pin = peripherals.pins.gpio27;
162/// # let channel = peripherals.rmt.channel0;
163///
164/// // WS2812B timing parameters.
165/// const WS2812_T0H_NS: Duration = Duration::from_nanos(400);
166/// const WS2812_T0L_NS: Duration = Duration::from_nanos(850);
167/// const WS2812_T1H_NS: Duration = Duration::from_nanos(800);
168/// const WS2812_T1L_NS: Duration = Duration::from_nanos(450);
169///
170/// let driver_config = TransmitConfig::new()
171///     .clock_divider(1); // Required parameter.
172/// let tx_driver = TxRmtDriver::new(channel, led_pin, &driver_config).unwrap();
173/// let driver = Ws2812Esp32RmtDriverBuilder::new_with_rmt_driver(tx_driver).unwrap()
174///    .encoder_duration(&WS2812_T0H_NS, &WS2812_T0L_NS, &WS2812_T1H_NS, &WS2812_T1L_NS).unwrap()
175///    .build().unwrap();
176/// ```
177pub struct Ws2812Esp32RmtDriverBuilder<'d> {
178    /// TxRMT driver.
179    tx: TxRmtDriver<'d>,
180    /// `u8`-to-`rmt_item32_t` Encoder
181    encoder: Option<Ws2812Esp32RmtItemEncoder>,
182}
183
184impl<'d> Ws2812Esp32RmtDriverBuilder<'d> {
185    /// Creates a new `Ws2812Esp32RmtDriverBuilder`.
186    pub fn new<C: RmtChannel>(
187        channel: impl Peripheral<P = C> + 'd,
188        pin: impl Peripheral<P = impl OutputPin> + 'd,
189    ) -> Result<Self, Ws2812Esp32RmtDriverError> {
190        let config = TransmitConfig::new().clock_divider(1);
191        let tx = TxRmtDriver::new(channel, pin, &config)?;
192
193        Self::new_with_rmt_driver(tx)
194    }
195
196    /// Creates a new `Ws2812Esp32RmtDriverBuilder` with `TxRmtDriver`.
197    pub fn new_with_rmt_driver(tx: TxRmtDriver<'d>) -> Result<Self, Ws2812Esp32RmtDriverError> {
198        Ok(Self { tx, encoder: None })
199    }
200
201    /// Sets the encoder duration times.
202    ///
203    /// # Arguments
204    ///
205    /// * `clock_hz` - The clock frequency.
206    /// * `t0h` - T0H duration time (0 code, high voltage time)
207    /// * `t0l` - T0L duration time (0 code, low voltage time)
208    /// * `t1h` - T1H duration time (1 code, high voltage time)
209    /// * `t1l` - T1L duration time (1 code, low voltage time)
210    ///
211    /// # Errors
212    ///
213    /// Returns an error if the encoder initialization failed.
214    pub fn encoder_duration(
215        mut self,
216        t0h: &Duration,
217        t0l: &Duration,
218        t1h: &Duration,
219        t1l: &Duration,
220    ) -> Result<Self, Ws2812Esp32RmtDriverError> {
221        let clock_hz = self.tx.counter_clock()?;
222        self.encoder = Some(Ws2812Esp32RmtItemEncoder::new(
223            clock_hz, t0h, t0l, t1h, t1l,
224        )?);
225        Ok(self)
226    }
227
228    /// Builds the `Ws2812Esp32RmtDriver`.
229    pub fn build(self) -> Result<Ws2812Esp32RmtDriver<'d>, Ws2812Esp32RmtDriverError> {
230        let encoder = if let Some(encoder) = self.encoder {
231            encoder
232        } else {
233            let clock_hz = self.tx.counter_clock()?;
234            Ws2812Esp32RmtItemEncoder::new(
235                clock_hz,
236                &WS2812_T0H_NS,
237                &WS2812_T0L_NS,
238                &WS2812_T1H_NS,
239                &WS2812_T1L_NS,
240            )?
241        };
242
243        Ok(Ws2812Esp32RmtDriver {
244            tx: self.tx,
245            encoder,
246            #[cfg(not(target_vendor = "espressif"))]
247            pixel_data: None,
248            #[cfg(not(target_vendor = "espressif"))]
249            phantom: Default::default(),
250        })
251    }
252}
253
254/// WS2812 ESP32 RMT driver wrapper.
255///
256/// # Examples
257///
258/// ```
259/// # #[cfg(not(target_vendor = "espressif"))]
260/// # use ws2812_esp32_rmt_driver::mock::esp_idf_hal;
261/// #
262/// use esp_idf_hal::peripherals::Peripherals;
263/// use ws2812_esp32_rmt_driver::driver::Ws2812Esp32RmtDriver;
264/// use ws2812_esp32_rmt_driver::driver::color::{LedPixelColor, LedPixelColorGrb24};
265///
266/// let peripherals = Peripherals::take().unwrap();
267/// let led_pin = peripherals.pins.gpio27;
268/// let channel = peripherals.rmt.channel0;
269/// let mut driver = Ws2812Esp32RmtDriver::new(channel, led_pin).unwrap();
270///
271/// // Single LED with RED color.
272/// let red = LedPixelColorGrb24::new_with_rgb(30, 0, 0);
273/// let pixel: [u8; 3] = red.as_ref().try_into().unwrap();
274/// assert_eq!(pixel, [0, 30, 0]);
275///
276/// driver.write_blocking(pixel.clone().into_iter()).unwrap();
277/// ```
278pub struct Ws2812Esp32RmtDriver<'d> {
279    /// TxRMT driver.
280    tx: TxRmtDriver<'d>,
281    /// `u8`-to-`rmt_item32_t` Encoder
282    encoder: Ws2812Esp32RmtItemEncoder,
283
284    /// Pixel binary array to be written
285    ///
286    /// If the target vendor does not equals to "espressif", pixel data is written into this
287    /// instead of genuine encoder.
288    #[cfg(not(target_vendor = "espressif"))]
289    pub pixel_data: Option<Vec<u8>>,
290    /// Dummy phantom to take care of lifetime for `pixel_data`.
291    #[cfg(not(target_vendor = "espressif"))]
292    phantom: PhantomData<&'d Option<Vec<u8>>>,
293}
294
295impl<'d> Ws2812Esp32RmtDriver<'d> {
296    /// Creates a WS2812 ESP32 RMT driver wrapper.
297    ///
298    /// RMT driver of `channel` shall be initialized and installed for `pin`.
299    /// `channel` shall be different between different `pin`.
300    ///
301    /// # Errors
302    ///
303    /// Returns an error if the RMT driver initialization failed.
304    pub fn new<C: RmtChannel>(
305        channel: impl Peripheral<P = C> + 'd,
306        pin: impl Peripheral<P = impl OutputPin> + 'd,
307    ) -> Result<Self, Ws2812Esp32RmtDriverError> {
308        Ws2812Esp32RmtDriverBuilder::new(channel, pin)?.build()
309    }
310
311    /// Creates a WS2812 ESP32 RMT driver wrapper with `TxRmtDriver`.
312    ///
313    /// The clock divider must be set to 1 for the `driver` configuration.
314    ///
315    /// ```
316    /// # #[cfg(not(target_vendor = "espressif"))]
317    /// # use ws2812_esp32_rmt_driver::mock::esp_idf_hal;
318    /// #
319    /// # use esp_idf_hal::peripherals::Peripherals;
320    /// # use esp_idf_hal::rmt::config::TransmitConfig;
321    /// # use esp_idf_hal::rmt::TxRmtDriver;
322    /// #
323    /// # let peripherals = Peripherals::take().unwrap();
324    /// # let led_pin = peripherals.pins.gpio27;
325    /// # let channel = peripherals.rmt.channel0;
326    /// #
327    /// let driver_config = TransmitConfig::new()
328    ///     .clock_divider(1); // Required parameter.
329    /// let driver = TxRmtDriver::new(channel, led_pin, &driver_config).unwrap();
330    /// ```
331    ///
332    /// # Errors
333    ///
334    /// Returns an error if the RMT driver initialization failed.
335    pub fn new_with_rmt_driver(tx: TxRmtDriver<'d>) -> Result<Self, Ws2812Esp32RmtDriverError> {
336        Ws2812Esp32RmtDriverBuilder::new_with_rmt_driver(tx)?.build()
337    }
338
339    /// Writes pixel data from a pixel-byte sequence to the IO pin.
340    ///
341    /// Byte count per LED pixel and channel order is not handled by this method.
342    /// The pixel data sequence has to be correctly laid out depending on the LED strip model.
343    ///
344    /// # Errors
345    ///
346    /// Returns an error if an RMT driver error occurred.
347    ///
348    /// # Warning
349    ///
350    /// Iteration of `pixel_sequence` happens inside an interrupt handler so beware of side-effects
351    /// that don't work in interrupt handlers.
352    /// See [esp_idf_hal::rmt::TxRmtDriver#start_iter_blocking()] for details.
353    pub fn write_blocking<'a, 'b, T>(
354        &'a mut self,
355        pixel_sequence: T,
356    ) -> Result<(), Ws2812Esp32RmtDriverError>
357    where
358        'b: 'a,
359        T: Iterator<Item = u8> + Send + 'b,
360    {
361        #[cfg(target_vendor = "espressif")]
362        {
363            let signal = self.encoder.encode_iter(pixel_sequence);
364            self.tx.start_iter_blocking(signal)?;
365        }
366        #[cfg(not(target_vendor = "espressif"))]
367        {
368            self.pixel_data = Some(pixel_sequence.collect());
369        }
370        Ok(())
371    }
372
373    /// Writes pixel data from a pixel-byte sequence to the IO pin.
374    ///
375    /// Byte count per LED pixel and channel order is not handled by this method.
376    /// The pixel data sequence has to be correctly laid out depending on the LED strip model.
377    ///
378    /// Note that this requires `pixel_sequence` to be [`Box`]ed for an allocation free version see [`Self::write_blocking`].
379    ///
380    /// # Errors
381    ///
382    /// Returns an error if an RMT driver error occurred.
383    ///
384    /// # Warning
385    ///
386    /// Iteration of `pixel_sequence` happens inside an interrupt handler so beware of side-effects
387    /// that don't work in interrupt handlers.
388    /// See [esp_idf_hal::rmt::TxRmtDriver#start_iter()] for details.
389    #[cfg(feature = "alloc")]
390    pub fn write<'b, T>(
391        &'static mut self,
392        pixel_sequence: T,
393    ) -> Result<(), Ws2812Esp32RmtDriverError>
394    where
395        T: Iterator<Item = u8> + Send + 'static,
396    {
397        #[cfg(target_vendor = "espressif")]
398        {
399            let signal = self.encoder.encode_iter(pixel_sequence);
400            self.tx.start_iter(signal)?;
401        }
402        #[cfg(not(target_vendor = "espressif"))]
403        {
404            self.pixel_data = Some(pixel_sequence.collect());
405        }
406        Ok(())
407    }
408}