1#![doc = document_features::document_features!()]
38#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
39#![deny(missing_docs)]
40#![no_std]
41
42use core::{fmt::Debug, marker::PhantomData, slice::IterMut};
43
44use esp_hal::{
45 Async, Blocking,
46 clock::Clocks,
47 gpio::{Level, interconnect::PeripheralOutput},
48 rmt::{Channel, Error as RmtError, PulseCode, Tx, TxChannelConfig, TxChannelCreator},
49};
50use rgb::Grb;
51use smart_leds_trait::{SmartLedsWrite, SmartLedsWriteAsync};
52
53const RMT_RAM_ONE_LED: usize = 3 * 8;
56const RMT_RAM_ONE_RBGW_LED: usize = 4 * 8;
57
58const SK68XX_CODE_PERIOD: u32 = 1250; const SK68XX_T0H_NS: u32 = 400; const SK68XX_T0L_NS: u32 = SK68XX_CODE_PERIOD - SK68XX_T0H_NS;
61const SK68XX_T1H_NS: u32 = 850; const SK68XX_T1L_NS: u32 = SK68XX_CODE_PERIOD - SK68XX_T1H_NS;
63
64#[derive(Debug)]
67#[cfg_attr(feature = "defmt", derive(defmt::Format))]
68pub enum LedAdapterError {
69 BufferSizeExceeded,
71 TransmissionError(RmtError),
73}
74
75impl From<RmtError> for LedAdapterError {
76 fn from(e: RmtError) -> Self {
77 LedAdapterError::TransmissionError(e)
78 }
79}
80
81fn led_pulses_for_clock(src_clock: u32) -> (PulseCode, PulseCode) {
82 (
83 PulseCode::new(
84 Level::High,
85 ((SK68XX_T0H_NS * src_clock) / 1000) as u16,
86 Level::Low,
87 ((SK68XX_T0L_NS * src_clock) / 1000) as u16,
88 ),
89 PulseCode::new(
90 Level::High,
91 ((SK68XX_T1H_NS * src_clock) / 1000) as u16,
92 Level::Low,
93 ((SK68XX_T1L_NS * src_clock) / 1000) as u16,
94 ),
95 )
96}
97
98fn led_config() -> TxChannelConfig {
99 TxChannelConfig::default()
100 .with_clk_divider(1)
101 .with_idle_output_level(Level::Low)
102 .with_carrier_modulation(false)
103 .with_idle_output(true)
104}
105
106fn convert_to_pulses(
107 value: &[u8],
108 mut_iter: &mut IterMut<PulseCode>,
109 pulses: (PulseCode, PulseCode),
110) -> Result<(), LedAdapterError> {
111 for v in value {
112 convert_rgb_channel_to_pulses(*v, mut_iter, pulses)?;
113 }
114 Ok(())
115}
116
117fn convert_rgb_channel_to_pulses(
118 channel_value: u8,
119 mut_iter: &mut IterMut<PulseCode>,
120 pulses: (PulseCode, PulseCode),
121) -> Result<(), LedAdapterError> {
122 for position in [128, 64, 32, 16, 8, 4, 2, 1] {
123 *mut_iter.next().ok_or(LedAdapterError::BufferSizeExceeded)? =
124 match channel_value & position {
125 0 => pulses.0,
126 _ => pulses.1,
127 }
128 }
129
130 Ok(())
131}
132
133pub const fn buffer_size(num_leds: usize) -> usize {
141 num_leds * RMT_RAM_ONE_LED + 1
143}
144
145pub const fn buffer_size_rgbw(num_leds: usize) -> usize {
153 num_leds * RMT_RAM_ONE_RBGW_LED + 1
155}
156
157#[macro_export]
165macro_rules! smart_led_buffer {
166 ( $num_leds: expr ) => {
167 [::esp_hal::rmt::PulseCode::end_marker(); $crate::buffer_size($num_leds)]
168 };
169 ( $num_leds: expr; RGBW ) => {
170 [::esp_hal::rmt::PulseCode::end_marker(); $crate::buffer_size_rgbw($num_leds)]
171 };
172}
173
174#[macro_export]
176#[deprecated]
177macro_rules! smartLedBuffer {
178 ( $num_leds: expr ) => {
179 smart_led_buffer!($num_leds);
180 };
181}
182
183pub struct SmartLedsAdapter<'ch, const BUFFER_SIZE: usize, Color = Grb<u8>> {
186 channel: Option<Channel<'ch, Blocking, Tx>>,
187 rmt_buffer: &'ch mut [PulseCode; BUFFER_SIZE],
188 pulses: (PulseCode, PulseCode),
189 color: PhantomData<Color>,
190}
191
192impl<'ch, const BUFFER_SIZE: usize> SmartLedsAdapter<'ch, BUFFER_SIZE, Grb<u8>> {
193 pub fn new<C, O>(channel: C, pin: O, rmt_buffer: &'ch mut [PulseCode; BUFFER_SIZE]) -> Self
195 where
196 O: PeripheralOutput<'ch>,
197 C: TxChannelCreator<'ch, Blocking>,
198 {
199 Self::new_with_color(channel, pin, rmt_buffer)
200 }
201}
202
203impl<'ch, const BUFFER_SIZE: usize, Color> SmartLedsAdapter<'ch, BUFFER_SIZE, Color>
204where
205 Color: rgb::ComponentSlice<u8>,
206{
207 pub fn new_with_color<C, O>(
209 channel: C,
210 pin: O,
211 rmt_buffer: &'ch mut [PulseCode; BUFFER_SIZE],
212 ) -> SmartLedsAdapter<'ch, BUFFER_SIZE, Color>
213 where
214 O: PeripheralOutput<'ch>,
215 C: TxChannelCreator<'ch, Blocking>,
216 {
217 let channel = channel.configure_tx(pin, led_config()).unwrap();
218
219 let src_clock = Clocks::get().apb_clock.as_mhz();
221
222 Self {
223 channel: Some(channel),
224 rmt_buffer,
225 pulses: led_pulses_for_clock(src_clock),
226 color: PhantomData,
227 }
228 }
229}
230
231impl<'ch, const BUFFER_SIZE: usize, Color> SmartLedsWrite
232 for SmartLedsAdapter<'ch, BUFFER_SIZE, Color>
233where
234 Color: rgb::ComponentSlice<u8>,
235{
236 type Error = LedAdapterError;
237 type Color = Color;
238
239 fn write<T, I>(&mut self, iterator: T) -> Result<(), Self::Error>
243 where
244 T: IntoIterator<Item = I>,
245 I: Into<Self::Color>,
246 {
247 let mut seq_iter = self.rmt_buffer.iter_mut();
249
250 for item in iterator {
254 convert_to_pulses(item.into().as_slice(), &mut seq_iter, self.pulses)?;
255 }
256
257 *seq_iter.next().ok_or(LedAdapterError::BufferSizeExceeded)? = PulseCode::end_marker();
259
260 let channel = self.channel.take().unwrap();
262 match channel.transmit(self.rmt_buffer)?.wait() {
263 Ok(chan) => {
264 self.channel = Some(chan);
265 Ok(())
266 }
267 Err((e, chan)) => {
268 self.channel = Some(chan);
269 Err(LedAdapterError::TransmissionError(e))
270 }
271 }
272 }
273}
274
275pub const fn buffer_size_async(num_leds: usize) -> usize {
285 num_leds * (RMT_RAM_ONE_LED + 1)
287}
288
289pub const fn buffer_size_async_rgbw(num_leds: usize) -> usize {
297 num_leds * (RMT_RAM_ONE_RBGW_LED + 1)
299}
300
301pub struct SmartLedsAdapterAsync<'ch, const BUFFER_SIZE: usize, Color = Grb<u8>> {
304 channel: Channel<'ch, Async, Tx>,
305 rmt_buffer: &'ch mut [PulseCode; BUFFER_SIZE],
306 pulses: (PulseCode, PulseCode),
307 color: PhantomData<Color>,
308}
309
310impl<'ch, const BUFFER_SIZE: usize> SmartLedsAdapterAsync<'ch, BUFFER_SIZE, Grb<u8>> {
311 pub fn new<C, O>(channel: C, pin: O, rmt_buffer: &'ch mut [PulseCode; BUFFER_SIZE]) -> Self
313 where
314 O: PeripheralOutput<'ch>,
315 C: TxChannelCreator<'ch, Async>,
316 {
317 Self::new_with_color(channel, pin, rmt_buffer)
318 }
319}
320
321impl<'ch, const BUFFER_SIZE: usize, Color> SmartLedsAdapterAsync<'ch, BUFFER_SIZE, Color>
322where
323 Color: rgb::ComponentSlice<u8>,
324{
325 pub fn new_with_color<C, O>(
327 channel: C,
328 pin: O,
329 rmt_buffer: &'ch mut [PulseCode; BUFFER_SIZE],
330 ) -> SmartLedsAdapterAsync<'ch, BUFFER_SIZE, Color>
331 where
332 O: PeripheralOutput<'ch>,
333 C: TxChannelCreator<'ch, Async>,
334 {
335 let channel = channel.configure_tx(pin, led_config()).unwrap();
336
337 let src_clock = Clocks::get().apb_clock.as_mhz();
339
340 Self {
341 channel,
342 rmt_buffer,
343 pulses: led_pulses_for_clock(src_clock),
344 color: PhantomData,
345 }
346 }
347
348 fn prepare_rmt_buffer<I: Into<Color>>(
349 &mut self,
350 iterator: impl IntoIterator<Item = I>,
351 ) -> Result<(), LedAdapterError> {
352 let mut seq_iter = self.rmt_buffer.iter_mut();
354
355 for item in iterator {
359 Self::convert_to_pulses(item.into().as_slice(), &mut seq_iter, self.pulses)?;
360 }
361 Ok(())
362 }
363
364 fn convert_to_pulses(
366 value: &[u8],
367 mut_iter: &mut IterMut<PulseCode>,
368 pulses: (PulseCode, PulseCode),
369 ) -> Result<(), LedAdapterError> {
370 for v in value {
371 convert_rgb_channel_to_pulses(*v, mut_iter, pulses)?;
372 }
373 *mut_iter.next().ok_or(LedAdapterError::BufferSizeExceeded)? = PulseCode::end_marker();
374 Ok(())
375 }
376}
377
378impl<'ch, const BUFFER_SIZE: usize, Color> SmartLedsWriteAsync
379 for SmartLedsAdapterAsync<'ch, BUFFER_SIZE, Color>
380where
381 Color: rgb::ComponentSlice<u8>,
382{
383 type Error = LedAdapterError;
384 type Color = Color;
385
386 async fn write<T, I>(&mut self, iterator: T) -> Result<(), Self::Error>
390 where
391 T: IntoIterator<Item = I>,
392 I: Into<Self::Color>,
393 {
394 self.prepare_rmt_buffer(iterator)?;
395 for chunk in self.rmt_buffer.chunks(RMT_RAM_ONE_LED + 1) {
396 self.channel
397 .transmit(chunk)
398 .await
399 .map_err(LedAdapterError::TransmissionError)?;
400 }
401 Ok(())
402 }
403}