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}