1#[cfg(feature = "async")]
22use blinksy::driver::ClocklessWriterAsync;
23use blinksy::{
24 driver::{clockless::ClocklessLed, ClocklessWriter},
25 util::bits::{word_to_bits_msb, Word},
26};
27use core::{fmt::Debug, marker::PhantomData};
28#[cfg(feature = "async")]
29use esp_hal::Async;
30use esp_hal::{
31 clock::Clocks,
32 gpio::{interconnect::PeripheralOutput, Level},
33 rmt::{
34 Channel, Error as RmtError, PulseCode, Tx, TxChannelConfig, TxChannelCreator,
35 CHANNEL_RAM_SIZE,
36 },
37 Blocking, DriverMode,
38};
39use heapless::Vec;
40
41use crate::util::chunked;
42
43pub const fn rmt_buffer_size<Led: ClocklessLed>(pixel_count: usize) -> usize {
44 pixel_count * Led::LED_CHANNELS.channel_count() * 8 + 1
45}
46
47#[derive(Debug)]
50#[cfg_attr(feature = "defmt", derive(defmt::Format))]
51pub enum ClocklessRmtError {
52 BufferSizeExceeded,
54 TransmissionError(RmtError),
56}
57
58pub struct ClocklessRmtBuilder<const RMT_BUFFER_SIZE: usize, Led, Chan, Pin> {
59 led: PhantomData<Led>,
60 channel: Chan,
61 pin: Pin,
62}
63
64impl Default for ClocklessRmtBuilder<CHANNEL_RAM_SIZE, (), (), ()> {
65 fn default() -> ClocklessRmtBuilder<CHANNEL_RAM_SIZE, (), (), ()> {
66 ClocklessRmtBuilder {
67 led: PhantomData,
68 channel: (),
69 pin: (),
70 }
71 }
72}
73
74impl<Led, Chan, Pin> ClocklessRmtBuilder<CHANNEL_RAM_SIZE, Led, Chan, Pin> {
75 pub fn with_rmt_buffer_size<const RMT_BUFFER_SIZE: usize>(
76 self,
77 ) -> ClocklessRmtBuilder<RMT_BUFFER_SIZE, Led, Chan, Pin> {
78 ClocklessRmtBuilder {
79 led: self.led,
80 channel: self.channel,
81 pin: self.pin,
82 }
83 }
84}
85
86impl<const RMT_BUFFER_SIZE: usize, Chan, Pin> ClocklessRmtBuilder<RMT_BUFFER_SIZE, (), Chan, Pin> {
87 pub fn with_led<Led>(self) -> ClocklessRmtBuilder<RMT_BUFFER_SIZE, Led, Chan, Pin> {
88 ClocklessRmtBuilder {
89 led: PhantomData,
90 channel: self.channel,
91 pin: self.pin,
92 }
93 }
94}
95
96impl<const RMT_BUFFER_SIZE: usize, Led, Pin> ClocklessRmtBuilder<RMT_BUFFER_SIZE, Led, (), Pin> {
97 pub fn with_channel<Chan>(
98 self,
99 channel: Chan,
100 ) -> ClocklessRmtBuilder<RMT_BUFFER_SIZE, Led, Chan, Pin> {
101 ClocklessRmtBuilder {
102 led: self.led,
103 channel,
104 pin: self.pin,
105 }
106 }
107}
108
109impl<const RMT_BUFFER_SIZE: usize, Led, Chan> ClocklessRmtBuilder<RMT_BUFFER_SIZE, Led, Chan, ()> {
110 pub fn with_pin<Pin>(self, pin: Pin) -> ClocklessRmtBuilder<RMT_BUFFER_SIZE, Led, Chan, Pin> {
111 ClocklessRmtBuilder {
112 led: self.led,
113 channel: self.channel,
114 pin,
115 }
116 }
117}
118
119impl<const RMT_BUFFER_SIZE: usize, Led, Chan, Pin>
120 ClocklessRmtBuilder<RMT_BUFFER_SIZE, Led, Chan, Pin>
121where
122 Led: ClocklessLed,
123 Led::Word: Word,
124{
125 pub fn build<'ch, Dm>(self) -> ClocklessRmt<RMT_BUFFER_SIZE, Led, Channel<'ch, Dm, Tx>>
126 where
127 Chan: TxChannelCreator<'ch, Dm>,
128 Pin: PeripheralOutput<'ch>,
129 Dm: DriverMode,
130 {
131 ClocklessRmt::new(self.channel, self.pin)
132 }
133}
134
135pub struct ClocklessRmt<const RMT_BUFFER_SIZE: usize, Led, TxChannel>
146where
147 Led: ClocklessLed,
148{
149 led: PhantomData<Led>,
150 channel: Option<TxChannel>,
151 pulses: (PulseCode, PulseCode, PulseCode),
152}
153
154impl<const RMT_BUFFER_SIZE: usize, Led, TxChannel> ClocklessRmt<RMT_BUFFER_SIZE, Led, TxChannel>
155where
156 Led: ClocklessLed,
157 Led::Word: Word,
158{
159 fn clock_divider() -> u8 {
160 1
161 }
162
163 fn setup_pulses() -> (PulseCode, PulseCode, PulseCode) {
164 let clocks = Clocks::get();
165 let freq_hz = clocks.apb_clock.as_hz() / Self::clock_divider() as u32;
166 let freq_mhz = freq_hz / 1_000_000;
167
168 let t_0h = ((Led::T_0H.to_nanos() * freq_mhz) / 1_000) as u16;
169 let t_0l = ((Led::T_0L.to_nanos() * freq_mhz) / 1_000) as u16;
170 let t_1h = ((Led::T_1H.to_nanos() * freq_mhz) / 1_000) as u16;
171 let t_1l = ((Led::T_1L.to_nanos() * freq_mhz) / 1_000) as u16;
172 let t_reset = ((Led::T_RESET.to_nanos() * freq_mhz) / 1_000) as u16;
173
174 (
175 PulseCode::new(Level::High, t_0h, Level::Low, t_0l),
176 PulseCode::new(Level::High, t_1h, Level::Low, t_1l),
177 PulseCode::new(Level::Low, t_reset, Level::Low, 0),
178 )
179 }
180
181 fn frame_pulses<const FRAME_BUFFER_SIZE: usize>(
182 &self,
183 frame: Vec<Led::Word, FRAME_BUFFER_SIZE>,
184 ) -> impl Iterator<Item = PulseCode> {
185 let pulses = self.pulses;
186 frame.into_iter().flat_map(move |word| {
187 word_to_bits_msb(word).map(move |bit| match bit {
188 false => pulses.0,
189 true => pulses.1,
190 })
191 })
192 }
193}
194
195impl<'ch, const RMT_BUFFER_SIZE: usize, Led, Dm>
196 ClocklessRmt<RMT_BUFFER_SIZE, Led, Channel<'ch, Dm, Tx>>
197where
198 Led: ClocklessLed,
199 Led::Word: Word,
200 Dm: DriverMode,
201{
202 pub fn new<C, O>(channel: C, pin: O) -> Self
213 where
214 C: TxChannelCreator<'ch, Dm>,
215 O: PeripheralOutput<'ch>,
216 {
217 let config = TxChannelConfig::default()
218 .with_clk_divider(Self::clock_divider())
219 .with_idle_output_level(Level::Low)
220 .with_idle_output(true);
221 let channel = channel.configure_tx(pin, config).unwrap();
222 let pulses = Self::setup_pulses();
223
224 Self {
225 led: PhantomData,
226 channel: Some(channel),
227 pulses,
228 }
229 }
230}
231
232impl<'ch, const RMT_BUFFER_SIZE: usize, Led>
233 ClocklessRmt<RMT_BUFFER_SIZE, Led, Channel<'ch, Blocking, Tx>>
234where
235 Led: ClocklessLed,
236{
237 fn transmit_blocking(&mut self, buffer: &[PulseCode]) -> Result<(), ClocklessRmtError> {
247 let channel = self.channel.take().unwrap();
248 match channel.transmit(buffer).unwrap().wait() {
249 Ok(chan) => {
250 self.channel = Some(chan);
251 Ok(())
252 }
253 Err((e, chan)) => {
254 self.channel = Some(chan);
255 Err(ClocklessRmtError::TransmissionError(e))
256 }
257 }
258 }
259}
260
261#[cfg(feature = "async")]
262impl<'ch, const RMT_BUFFER_SIZE: usize, Led>
263 ClocklessRmt<RMT_BUFFER_SIZE, Led, Channel<'ch, Async, Tx>>
264where
265 Led: ClocklessLed,
266{
267 async fn transmit_async(&mut self, buffer: &[PulseCode]) -> Result<(), ClocklessRmtError> {
277 let channel = self.channel.as_mut().unwrap();
278 channel
279 .transmit(buffer)
280 .await
281 .map_err(ClocklessRmtError::TransmissionError)
282 }
283}
284
285impl<'ch, const RMT_BUFFER_SIZE: usize, Led> ClocklessWriter<Led>
286 for ClocklessRmt<RMT_BUFFER_SIZE, Led, Channel<'ch, Blocking, Tx>>
287where
288 Led: ClocklessLed,
289 Led::Word: Word,
290{
291 type Error = ClocklessRmtError;
292
293 fn write<const FRAME_BUFFER_SIZE: usize>(
294 &mut self,
295 frame: Vec<Led::Word, FRAME_BUFFER_SIZE>,
296 ) -> Result<(), Self::Error> {
297 let rmt_pulses = self.frame_pulses(frame);
298 for mut rmt_buffer in chunked::<_, RMT_BUFFER_SIZE>(rmt_pulses, RMT_BUFFER_SIZE - 1) {
299 rmt_buffer.push(PulseCode::end_marker()).unwrap();
301 self.transmit_blocking(&rmt_buffer)?;
302 }
303
304 Ok(())
305 }
306}
307
308#[cfg(feature = "async")]
309impl<'ch, const RMT_BUFFER_SIZE: usize, Led> ClocklessWriterAsync<Led>
310 for ClocklessRmt<RMT_BUFFER_SIZE, Led, Channel<'ch, Async, Tx>>
311where
312 Led: ClocklessLed,
313 Led::Word: Word,
314{
315 type Error = ClocklessRmtError;
316
317 async fn write<const FRAME_BUFFER_SIZE: usize>(
318 &mut self,
319 frame: Vec<Led::Word, FRAME_BUFFER_SIZE>,
320 ) -> Result<(), Self::Error> {
321 let rmt_pulses = self.frame_pulses(frame);
322 for mut rmt_buffer in chunked::<_, RMT_BUFFER_SIZE>(rmt_pulses, RMT_BUFFER_SIZE - 1) {
323 rmt_buffer.push(PulseCode::end_marker()).unwrap();
325 self.transmit_async(&rmt_buffer).await?;
326 }
327
328 Ok(())
329 }
330}