ws2812_flexio/flexio/
driver.rs

1use core::{future::Future, task::Poll};
2
3use imxrt_ral as ral;
4
5use ral::{flexio, Valid};
6
7use super::{
8    dma::WS2812Dma, flexio_configurator::FlexIOConfigurator,
9    idle_timer_finished_watcher::IdleTimerFinishedWatcher, interleaved_pixels::InterleavedPixels,
10    maybe_own::MaybeOwn, InterruptHandler, InterruptHandlerData, PreprocessedPixels, WS2812Driver,
11    WriteDmaResult,
12};
13use crate::{errors, pixelstream::PixelStreamRef, Pins};
14
15impl<const N: u8, const L: usize, PINS: Pins<N, L>> WS2812Driver<N, L, PINS>
16where
17    flexio::Instance<N>: Valid,
18{
19    /// Initializes the FlexIO driver.
20    ///
21    /// IMPORTANT! Make sure that the clock input of the FlexIO instance is at 16MHz
22    /// prior to calling this function.
23    ///
24    /// Note that not all amounts of pins are always supported.
25    /// The limiting factor is most likely the number of FlexIO timers;
26    /// we need two timers plus an additional two per pin.
27    ///
28    /// For example, if the FlexIO instance has 8 timers, it supports up to 3 pins.
29    pub fn init(
30        flexio: flexio::Instance<N>,
31        mut pins: PINS,
32    ) -> Result<Self, errors::WS2812InitError> {
33        // Parameter check
34        let (version_major, version_minor, available_feature_set) =
35            ral::read_reg!(ral::flexio, flexio, VERID, MAJOR, MINOR, FEATURE);
36        let (available_triggers, available_pins, available_timers, available_shifters) =
37            ral::read_reg!(ral::flexio, flexio, PARAM, TRIGGER, PIN, TIMER, SHIFTER);
38
39        // Always u8 because our pins list is &[u8].
40        let available_pins = available_pins as u8;
41
42        log::debug!("Initializing FlexIO #{}.", N);
43        log::debug!("    Version: {}.{}", version_major, version_minor);
44        log::debug!("    Feature Set: {}", available_feature_set);
45        log::debug!("    Peripherals:");
46        log::debug!("        {} triggers", available_triggers);
47        log::debug!("        {} pins", available_pins);
48        log::debug!("        {} timers", available_timers);
49        log::debug!("        {} shifters", available_shifters);
50        log::debug!("Output pins: {:?}", PINS::FLEXIO_PIN_OFFSETS);
51
52        if available_shifters < 1 {
53            return Err(errors::WS2812InitError::NotEnoughShifters);
54        }
55
56        if available_timers < 2 + u32::from(PINS::PIN_COUNT) * 2 {
57            return Err(errors::WS2812InitError::NotEnoughTimers);
58        }
59
60        //////////// Configure FlexIO registers /////////////////
61        let mut flexio = FlexIOConfigurator::new(flexio);
62
63        // Find 4 consecutive pins for the shifter output
64        let shifter_output_start_pin = {
65            let mut start = 0;
66            let mut found = false;
67            for i in 0..available_pins {
68                if PINS::FLEXIO_PIN_OFFSETS.contains(&i) {
69                    start = i + 1;
70                } else if i - start >= 3 {
71                    // We found 4 consecutive free pins!
72                    found = true;
73                    break;
74                }
75            }
76            if !found {
77                return Err(errors::WS2812InitError::NeedFourConsecutiveInternalPins);
78            }
79            start
80        };
81
82        // Find a free pin for the shift timer output
83        let shift_timer_output_pin = {
84            let mut found_pin = None;
85
86            for i in 0..available_pins {
87                if !PINS::FLEXIO_PIN_OFFSETS.contains(&i)
88                    && !(shifter_output_start_pin..shifter_output_start_pin + 4).contains(&i)
89                {
90                    found_pin = Some(i);
91                    break;
92                }
93            }
94
95            if let Some(pin) = found_pin {
96                pin
97            } else {
98                return Err(errors::WS2812InitError::NotEnoughPins);
99            }
100        };
101
102        let data_shifter = Self::get_shifter_id();
103        let shifter_timer = Self::get_shifter_timer_id();
104        let idle_timer = Self::get_idle_timer_id();
105
106        flexio.configure_shifter(data_shifter, shifter_timer, shifter_output_start_pin);
107        flexio.configure_shift_timer(shifter_timer, data_shifter, shift_timer_output_pin);
108        flexio.configure_idle_timer(idle_timer, shift_timer_output_pin, None);
109
110        for (pin_pos, pin_id) in PINS::FLEXIO_PIN_OFFSETS.iter().copied().enumerate() {
111            let pin_pos = pin_pos.try_into().unwrap();
112            let low_bit_timer = Self::get_low_bit_timer_id(pin_pos);
113            let high_bit_timer = Self::get_high_bit_timer_id(pin_pos);
114
115            let neopixel_output_pin = pin_id;
116
117            flexio.configure_low_bit_timer(
118                low_bit_timer,
119                shift_timer_output_pin,
120                neopixel_output_pin,
121            );
122            flexio.configure_high_bit_timer(
123                high_bit_timer,
124                shifter_output_start_pin + pin_pos,
125                neopixel_output_pin,
126            );
127        }
128
129        // Configure pins and create driver object
130        pins.configure();
131
132        // Finish and create watcher
133        let flexio = flexio.finish();
134        let inner = MaybeOwn::new(InterruptHandlerData {
135            finished_watcher: IdleTimerFinishedWatcher::new(flexio, Self::get_idle_timer_id()),
136        });
137
138        Ok(Self { _pins: pins, inner })
139    }
140
141    const fn get_shifter_id() -> u8 {
142        0
143    }
144
145    const fn get_shifter_timer_id() -> u8 {
146        0
147    }
148    const fn get_idle_timer_id() -> u8 {
149        1
150    }
151
152    const fn get_low_bit_timer_id(pin_pos: u8) -> u8 {
153        2 * pin_pos + 2
154    }
155    const fn get_high_bit_timer_id(pin_pos: u8) -> u8 {
156        2 * pin_pos + 3
157    }
158
159    fn flexio(&self) -> &imxrt_ral::flexio::Instance<N> {
160        self.inner.get().finished_watcher.flexio()
161    }
162
163    fn shift_buffer_empty(&self) -> bool {
164        let mask = 1u32 << Self::get_shifter_id();
165        (ral::read_reg!(ral::flexio, self.flexio(), SHIFTSTAT) & mask) != 0
166    }
167
168    fn fill_shift_buffer(&self, data: u32) {
169        let buf_id = usize::from(Self::get_shifter_id());
170        ral::write_reg!(ral::flexio, self.flexio(), SHIFTBUFBIS[buf_id], data);
171    }
172
173    /// Take the interrupt handler callback from the driver.
174    ///
175    /// # Arguments
176    ///
177    /// * `storage` - Static memory required by the function to work. See [`InterruptHandlerData`] for more information.
178    ///
179    /// For correct functionality of [`write_dma()`](WS2812Driver::write_dma) in
180    /// waker-based async runtimes (like RTIC 2), it is required to invoke the returned
181    /// [`InterruptHandler`] every time an interrupt of the given FlexIO peripheral happens.
182    pub fn take_interrupt_handler(
183        &mut self,
184        storage: &'static mut Option<InterruptHandlerData<N>>,
185    ) -> InterruptHandler<N> {
186        let mask = 1u32 << Self::get_idle_timer_id();
187        imxrt_ral::write_reg!(imxrt_ral::flexio, self.flexio(), TIMIEN, mask);
188
189        InterruptHandler {
190            data: self.inner.convert_to_static_ref(storage),
191        }
192    }
193
194    /// Writes pixels to an LED strip.
195    ///
196    /// If the strips are of different length, the shorter ones will be padded
197    /// with `0` to match the longest strip.
198    ///
199    /// For technical reasons, an additional `[0, 0, 0]` pixel will be added at
200    /// the of the transmission.
201    pub fn write(&mut self, data: [&mut dyn PixelStreamRef; L]) {
202        // Wait for the buffer to idle and clear timer overflow flag
203        while !self.shift_buffer_empty() {}
204        self.inner.get().finished_watcher.clear();
205
206        // Write data
207        for elem in InterleavedPixels::new(data) {
208            self.fill_shift_buffer(elem);
209            while !self.shift_buffer_empty() {}
210        }
211
212        // Wait for transfer finished
213        while !self.inner.get().finished_watcher.poll() {}
214    }
215
216    /// Writes pixels to an LED strip.
217    ///
218    /// In contrast to [`write()`](write), this one performs the actual copy
219    /// via DMA, whilst allowing for something else (like the next frame) to be
220    /// computed concurrently.
221    ///
222    /// # Arguments
223    ///
224    /// * `data` - Preprocessed pixel data to send to the LED strips
225    /// * `dma` - The dma channel that should be used to transmit the data
226    /// * `dma_signal_id` - The signal the FlexIO unit uses to communicate with the DMA.
227    ///                     This is chip specific and must therefore be supplied by the user.
228    ///                     The value can be found in the reference manual.
229    /// * `concurrent_action` - A function that will be executed while the pixels get transferred.
230    ///                         Typically used to render the next frame, so it can be
231    ///                         transmitted afterwards without a delay, to achieve the maximum possible
232    ///                         framerate.
233    ///
234    /// For technical reasons, an additional `[0, 0, 0]` pixel will be added at
235    /// the of the transmission.
236    pub async fn write_dma<F, R, const N2: usize, const P: usize>(
237        &mut self,
238        data: &PreprocessedPixels<N2, L, P>,
239        dma: &mut imxrt_dma::channel::Channel,
240        dma_signal_id: u32,
241        concurrent_action: F,
242    ) -> Result<WriteDmaResult<R>, imxrt_dma::Error>
243    where
244        F: Future<Output = R>,
245    {
246        // Wait for the buffer to idle.
247        // In normal usage, waiting here shouldn't happen;
248        // this is just to make sure.
249        while !self.shift_buffer_empty() {
250            cassette::yield_now().await;
251        }
252        self.inner.get().finished_watcher.clear();
253
254        let result = {
255            // Write data
256            let data = data.get_dma_data();
257            let mut destination =
258                WS2812Dma::new(self.flexio(), Self::get_shifter_id(), dma_signal_id);
259            let mut write =
260                core::pin::pin!(imxrt_dma::peripheral::write(dma, data, &mut destination));
261
262            let mut dma_finished = false;
263            if let Poll::Ready(s) = futures::poll!(&mut write) {
264                s?;
265                dma_finished = true;
266            }
267
268            // Execute function
269            let result = concurrent_action.await;
270
271            // Finish write
272            if !dma_finished {
273                // Query once to find out if we potentially lagged
274                if let Poll::Ready(s) = futures::poll!(&mut write) {
275                    s?;
276                    dma_finished = true;
277                } else {
278                    write.await?;
279                }
280            }
281
282            WriteDmaResult {
283                result,
284                lagged: dma_finished,
285            }
286        };
287
288        // Wait for transfer finished
289        while !self.shift_buffer_empty() {
290            self.inner.get().finished_watcher.finished().await;
291        }
292        self.inner.get().finished_watcher.finished().await;
293
294        Ok(result)
295    }
296
297    /// Same as [`write_dma()`](WS2812Driver::write_dma), but blocks until completion.
298    ///
299    /// Do not use this function in an async context as it will busy-wait
300    /// internally.
301    pub fn write_dma_blocking<F, R, const N2: usize, const P: usize>(
302        &mut self,
303        data: &PreprocessedPixels<N2, L, P>,
304        dma: &mut imxrt_dma::channel::Channel,
305        dma_signal_id: u32,
306        concurrent_action: F,
307    ) -> Result<WriteDmaResult<R>, imxrt_dma::Error>
308    where
309        F: FnOnce() -> R,
310    {
311        cassette::Cassette::new(core::pin::pin!(self.write_dma(
312            data,
313            dma,
314            dma_signal_id,
315            async { concurrent_action() }
316        )))
317        .block_on()
318    }
319}