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;
13use esp_idf_hal::{
14    gpio::OutputPin,
15    peripheral::Peripheral,
16    rmt::{config::TransmitConfig, RmtChannel, TxRmtDriver},
17};
18#[cfg(target_vendor = "espressif")]
19use esp_idf_hal::{
20    rmt::{PinState, Pulse, Symbol},
21    units::Hertz,
22};
23
24#[cfg(not(target_vendor = "espressif"))]
25use crate::mock::esp_idf_sys;
26use esp_idf_sys::EspError;
27
28/// T0H duration time (0 code, high voltage time)
29const WS2812_T0H_NS: Duration = Duration::from_nanos(400);
30/// T0L duration time (0 code, low voltage time)
31const WS2812_T0L_NS: Duration = Duration::from_nanos(850);
32/// T1H duration time (1 code, high voltage time)
33const WS2812_T1H_NS: Duration = Duration::from_nanos(800);
34/// T1L duration time (1 code, low voltage time)
35const WS2812_T1L_NS: Duration = Duration::from_nanos(450);
36
37/// Converter to a sequence of RMT items.
38#[repr(C)]
39#[cfg(target_vendor = "espressif")]
40struct Ws2812Esp32RmtItemEncoder {
41    /// The RMT item that represents a 0 code.
42    bit0: Symbol,
43    /// The RMT item that represents a 1 code.
44    bit1: Symbol,
45}
46
47#[cfg(target_vendor = "espressif")]
48impl Ws2812Esp32RmtItemEncoder {
49    /// Creates a new encoder with the given clock frequency.
50    ///
51    /// # Arguments
52    ///
53    /// * `clock_hz` - The clock frequency.
54    ///
55    /// # Errors
56    ///
57    /// Returns an error if the clock frequency is invalid or if the RMT item encoder cannot be created.
58    fn new(clock_hz: Hertz) -> Result<Self, EspError> {
59        let (bit0, bit1) = (
60            Symbol::new(
61                Pulse::new_with_duration(clock_hz, PinState::High, &WS2812_T0H_NS)?,
62                Pulse::new_with_duration(clock_hz, PinState::Low, &WS2812_T0L_NS)?,
63            ),
64            Symbol::new(
65                Pulse::new_with_duration(clock_hz, PinState::High, &WS2812_T1H_NS)?,
66                Pulse::new_with_duration(clock_hz, PinState::Low, &WS2812_T1L_NS)?,
67            ),
68        );
69
70        Ok(Self { bit0, bit1 })
71    }
72
73    /// Encodes a block of data as a sequence of RMT items.
74    ///
75    /// # Arguments
76    ///
77    /// * `src` - The block of data to encode.
78    ///
79    /// # Returns
80    ///
81    /// An iterator over the RMT items that represent the encoded data.
82    #[inline]
83    fn encode_iter<'a, 'b, T>(&'a self, src: T) -> impl Iterator<Item = Symbol> + Send + 'a
84    where
85        'b: 'a,
86        T: Iterator<Item = u8> + Send + 'b,
87    {
88        src.flat_map(move |v| {
89            (0..(u8::BITS as usize)).map(move |i| {
90                if v & (1 << (7 - i)) != 0 {
91                    self.bit1
92                } else {
93                    self.bit0
94                }
95            })
96        })
97    }
98}
99
100/// WS2812 ESP32 RMT Driver error.
101#[derive(Debug)]
102#[repr(transparent)]
103pub struct Ws2812Esp32RmtDriverError {
104    source: EspError,
105}
106
107#[cfg(not(feature = "std"))]
108impl Ws2812Esp32RmtDriverError {
109    /// The `EspError` source of this error, if any.
110    ///
111    /// This is a workaround function until `core::error::Error` added to `esp_sys::EspError`.
112    pub fn source(&self) -> Option<&EspError> {
113        Some(&self.source)
114    }
115}
116
117impl Error for Ws2812Esp32RmtDriverError {
118    fn source(&self) -> Option<&(dyn Error + 'static)> {
119        #[cfg(feature = "std")]
120        {
121            Some(&self.source)
122        }
123        #[cfg(not(feature = "std"))]
124        {
125            None
126        }
127    }
128}
129
130impl fmt::Display for Ws2812Esp32RmtDriverError {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        self.source.fmt(f)
133    }
134}
135
136impl From<EspError> for Ws2812Esp32RmtDriverError {
137    fn from(source: EspError) -> Self {
138        Self { source }
139    }
140}
141
142/// WS2812 ESP32 RMT driver wrapper.
143///
144/// # Examples
145///
146/// ```
147/// #[cfg(not(target_vendor = "espressif"))]
148/// use ws2812_esp32_rmt_driver::mock::esp_idf_hal;
149///
150/// use esp_idf_hal::peripherals::Peripherals;
151/// use ws2812_esp32_rmt_driver::driver::Ws2812Esp32RmtDriver;
152/// use ws2812_esp32_rmt_driver::driver::color::{LedPixelColor, LedPixelColorGrb24};
153///
154/// let peripherals = Peripherals::take().unwrap();
155/// let led_pin = peripherals.pins.gpio27;
156/// let channel = peripherals.rmt.channel0;
157/// let mut driver = Ws2812Esp32RmtDriver::new(channel, led_pin).unwrap();
158///
159/// // Single LED with RED color.
160/// let red = LedPixelColorGrb24::new_with_rgb(30, 0, 0);
161/// let pixel: [u8; 3] = red.as_ref().try_into().unwrap();
162/// assert_eq!(pixel, [0, 30, 0]);
163///
164/// driver.write_blocking(pixel.clone().into_iter()).unwrap();
165/// ```
166pub struct Ws2812Esp32RmtDriver<'d> {
167    /// TxRMT driver.
168    tx: TxRmtDriver<'d>,
169    /// `u8`-to-`rmt_item32_t` Encoder
170    #[cfg(target_vendor = "espressif")]
171    encoder: Ws2812Esp32RmtItemEncoder,
172
173    /// Pixel binary array to be written
174    ///
175    /// If the target vendor does not equals to "espressif", pixel data is written into this
176    /// instead of genuine encoder.
177    #[cfg(not(target_vendor = "espressif"))]
178    pub pixel_data: Option<Vec<u8>>,
179    /// Dummy phantom to take care of lifetime for `pixel_data`.
180    #[cfg(not(target_vendor = "espressif"))]
181    phantom: PhantomData<&'d Option<Vec<u8>>>,
182}
183
184impl<'d> Ws2812Esp32RmtDriver<'d> {
185    /// Creates a WS2812 ESP32 RMT driver wrapper.
186    ///
187    /// RMT driver of `channel` shall be initialized and installed for `pin`.
188    /// `channel` shall be different between different `pin`.
189    ///
190    /// # Errors
191    ///
192    /// Returns an error if the RMT driver initialization failed.
193    pub fn new<C: RmtChannel>(
194        channel: impl Peripheral<P = C> + 'd,
195        pin: impl Peripheral<P = impl OutputPin> + 'd,
196    ) -> Result<Self, Ws2812Esp32RmtDriverError> {
197        #[cfg(target_vendor = "espressif")]
198        {
199            let config = TransmitConfig::new().clock_divider(1);
200            let tx = TxRmtDriver::new(channel, pin, &config)?;
201
202            Self::new_with_rmt_driver(tx)
203        }
204        #[cfg(not(target_vendor = "espressif"))] // Mock implement
205        {
206            let config = TransmitConfig::new();
207            let tx = TxRmtDriver::new(channel, pin, &config)?;
208            Ok(Self {
209                tx,
210                pixel_data: None,
211                phantom: Default::default(),
212            })
213        }
214    }
215
216    pub fn new_with_rmt_driver(tx: TxRmtDriver<'d>) -> Result<Self, Ws2812Esp32RmtDriverError> {
217        #[cfg(target_vendor = "espressif")]
218        {
219            let clock_hz = tx.counter_clock()?;
220            let encoder = Ws2812Esp32RmtItemEncoder::new(clock_hz)?;
221
222            Ok(Self { tx, encoder })
223        }
224        #[cfg(not(target_vendor = "espressif"))] // Mock implement
225        {
226            Ok(Self {
227                tx,
228                pixel_data: None,
229                phantom: Default::default(),
230            })
231        }
232    }
233
234    /// Writes pixel data from a pixel-byte sequence to the IO pin.
235    ///
236    /// Byte count per LED pixel and channel order is not handled by this method.
237    /// The pixel data sequence has to be correctly laid out depending on the LED strip model.
238    ///
239    /// # Errors
240    ///
241    /// Returns an error if an RMT driver error occurred.
242    ///
243    /// # Warning
244    ///
245    /// Iteration of `pixel_sequence` happens inside an interrupt handler so beware of side-effects
246    /// that don't work in interrupt handlers.
247    /// See [esp_idf_hal::rmt::TxRmtDriver#start_iter_blocking()] for details.
248    pub fn write_blocking<'a, 'b, T>(
249        &'a mut self,
250        pixel_sequence: T,
251    ) -> Result<(), Ws2812Esp32RmtDriverError>
252    where
253        'b: 'a,
254        T: Iterator<Item = u8> + Send + 'b,
255    {
256        #[cfg(target_vendor = "espressif")]
257        {
258            let signal = self.encoder.encode_iter(pixel_sequence);
259            self.tx.start_iter_blocking(signal)?;
260        }
261        #[cfg(not(target_vendor = "espressif"))]
262        {
263            self.pixel_data = Some(pixel_sequence.collect());
264        }
265        Ok(())
266    }
267
268    /// Writes pixel data from a pixel-byte sequence to the IO pin.
269    ///
270    /// Byte count per LED pixel and channel order is not handled by this method.
271    /// The pixel data sequence has to be correctly laid out depending on the LED strip model.
272    ///
273    /// Note that this requires `pixel_sequence` to be [`Box`]ed for an allocation free version see [`Self::write_blocking`].
274    ///
275    /// # Errors
276    ///
277    /// Returns an error if an RMT driver error occurred.
278    ///
279    /// # Warning
280    ///
281    /// Iteration of `pixel_sequence` happens inside an interrupt handler so beware of side-effects
282    /// that don't work in interrupt handlers.
283    /// See [esp_idf_hal::rmt::TxRmtDriver#start_iter()] for details.
284    #[cfg(feature = "alloc")]
285    pub fn write<'b, T>(
286        &'static mut self,
287        pixel_sequence: T,
288    ) -> Result<(), Ws2812Esp32RmtDriverError>
289    where
290        T: Iterator<Item = u8> + Send + 'static,
291    {
292        #[cfg(target_vendor = "espressif")]
293        {
294            let signal = self.encoder.encode_iter(pixel_sequence);
295            self.tx.start_iter(signal)?;
296        }
297        #[cfg(not(target_vendor = "espressif"))]
298        {
299            self.pixel_data = Some(pixel_sequence.collect());
300        }
301        Ok(())
302    }
303}