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}