ws2812_flexio/flexio/
preprocessed_pixels.rs

1use crate::pixelstream::PixelStreamRef;
2
3use super::interleaved_pixels::InterleavedPixels;
4
5/// A buffer that preprocesses pixel data for FlexIO DMA usage.
6///
7/// # Generics:
8///
9/// * `N` - the number of pixels the buffer can hold
10/// * `L` - the number of LED strips
11/// * `P` - the number of bytes per pixel
12#[derive(Debug)]
13#[repr(C, align(4))]
14pub struct PreprocessedPixels<const N: usize, const L: usize, const P: usize = 3> {
15    /// Start with a `u32`, for 32bit alignment
16    len: u32,
17    /// The data. Would ideally be `[u32; P*N]`, but const expressions aren't there yet.
18    /// So we need to trick it with a pointer reinterpret cast later.
19    ///
20    /// Note that this struct always stores data for four LED strips, even when less are used.
21    /// The unused data is filled with zeros. That's just how the driver works.
22    data: [[u32; P]; N],
23    /// Zero termination of the data array.
24    /// Be sure to use the same element size as `data`, because otherwise it might introduce padding.
25    /// And I'm quite certain that reading from padding is undefined behaviour.
26    zero_termination: [u32; P],
27}
28
29impl<const N: usize, const L: usize, const P: usize> PreprocessedPixels<N, L, P> {
30    /// Creates a new PreprocessedPixels buffer.
31    pub const fn new() -> Self {
32        Self {
33            len: 0,
34            data: [[0; P]; N],
35            zero_termination: [0; P],
36        }
37    }
38
39    /// The amount of pixels that fit into this buffer
40    pub fn capacity(&self) -> usize {
41        N
42    }
43
44    fn get_data_mut(&mut self) -> &mut [u32] {
45        let ptr = self.data.as_mut_ptr().cast();
46        let len = P * N;
47
48        /* SAFETY
49            Our data is contiguous, so we can cast freely between [[u32;X];Y] and [u32;X*Y].
50        */
51        unsafe { core::slice::from_raw_parts_mut(ptr, len) }
52    }
53
54    pub(crate) fn get_dma_data(&self) -> &[u32] {
55        let ptr = self.data.as_ptr().cast();
56        let len = (P * N).min(self.len as usize) + P;
57
58        /* SAFETY
59            Our data is contiguous, so we can cast freely between [[u32;X];Y] and [u32;X*Y].
60            The + P is also safe, because our `zero_termination` is directly after it, no padding bytes.
61        */
62        unsafe { core::slice::from_raw_parts(ptr, len) }
63    }
64
65    /// Prepares a set of pixels for transmission to the LED strip.
66    pub fn prepare_pixels(&mut self, pixels: [&mut dyn PixelStreamRef; L]) {
67        let data = self.get_data_mut();
68
69        let mut len = 0;
70        for (d, pixel) in data.iter_mut().zip(InterleavedPixels::new(pixels)) {
71            *d = pixel;
72            len += 1;
73        }
74
75        data[len..].fill(0);
76
77        self.len = len as u32;
78    }
79}
80
81impl<const N: usize, const P: usize> Default for PreprocessedPixels<N, P> {
82    fn default() -> Self {
83        Self::new()
84    }
85}