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}