FastLED 3.10.8
Loading...
Searching...
No Matches
transposition.cpp.hpp
Go to the documentation of this file.
1
3
5#include "fl/math/math.h"
6#include "fl/stl/vector.h"
7#include "fl/stl/noexcept.h"
8
10
11namespace fl {
12
13// ============================================================================
14// Core 8x1 Bit Transpose Implementation
15// ============================================================================
16
19
20void FL_OPTIMIZE_FUNCTION FL_IRAM transpose8x1_noinline(unsigned char *A, unsigned char *B) FL_NO_EXCEPT {
21 u32 x, y, t;
22
23 // Load the array and pack it into x and y.
24 y = *(u32*)(A);
25 x = *(u32*)(A+4);
26
27 // pre-transform x
28 t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
29 t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
30
31 // pre-transform y
32 t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
33 t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
34
35 // final transform
36 t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
37 y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
38 x = t;
39
40 *((u32*)B) = y;
41 *((u32*)(B+4)) = x;
42}
43
45
46// ============================================================================
47// SPI Multi-Lane Transposer Implementation
48// ============================================================================
49
50// ----------------------------------------------------------------------------
51// 2-Way Transpose (Dual-SPI)
52// ----------------------------------------------------------------------------
53
55 const fl::optional<LaneData>& lane1,
56 fl::span<u8> output,
57 const char** error) FL_NO_EXCEPT {
58 // Validate output buffer size (must be divisible by 2)
59 if (output.size() % 2 != 0) {
60 if (error) {
61 *error = "Output buffer size must be divisible by 2";
62 }
63 return false;
64 }
65
66 // Calculate max lane size from output buffer
67 const size_t max_size = output.size() / 2;
68
69 // Handle empty case
70 if (max_size == 0) {
71 if (error) {
72 *error = nullptr; // No error, just empty
73 }
74 return true;
75 }
76
77 // Create array of lane references for easier iteration
78 const fl::optional<LaneData>* lanes[2] = {&lane0, &lane1};
79
80 // Determine default padding byte from first available lane
81 u8 default_padding = 0x00;
82 for (size_t i = 0; i < 2; i++) {
83 if (lanes[i]->has_value() && !(*lanes[i])->padding_frame.empty()) {
84 default_padding = (*lanes[i])->padding_frame[0];
85 break;
86 }
87 }
88
89 // Gather all bytes from each lane into temporary buffers
90 fl::vector<u8> lane0_buffer(max_size);
91 fl::vector<u8> lane1_buffer(max_size);
92
93 for (size_t byte_idx = 0; byte_idx < max_size; byte_idx++) {
94 lane0_buffer[byte_idx] = lanes[0]->has_value() ?
95 getLaneByte(**lanes[0], byte_idx, max_size) : default_padding;
96 lane1_buffer[byte_idx] = lanes[1]->has_value() ?
97 getLaneByte(**lanes[1], byte_idx, max_size) : default_padding;
98 }
99
100 // Perform transposition using ISR-safe primitive
101 transpose_2lane_inline(lane0_buffer.data(), lane1_buffer.data(), output.data(), max_size);
102
103 if (error) {
104 *error = nullptr; // Success, no error
105 }
106 return true;
107}
108
109// interleave_byte_2way removed - now uses transpose_2lane_inline() primitive
110
111// ----------------------------------------------------------------------------
112// 4-Way Transpose (Quad-SPI)
113// ----------------------------------------------------------------------------
114
116 const fl::optional<LaneData>& lane1,
117 const fl::optional<LaneData>& lane2,
118 const fl::optional<LaneData>& lane3,
119 fl::span<u8> output,
120 const char** error) FL_NO_EXCEPT {
121 // Validate output buffer size (must be divisible by 4)
122 if (output.size() % 4 != 0) {
123 if (error) {
124 *error = "Output buffer size must be divisible by 4";
125 }
126 return false;
127 }
128
129 // Calculate max lane size from output buffer
130 const size_t max_size = output.size() / 4;
131
132 // Handle empty case
133 if (max_size == 0) {
134 if (error) {
135 *error = nullptr; // No error, just empty
136 }
137 return true;
138 }
139
140 // Create array of lane references for easier iteration
141 const fl::optional<LaneData>* lanes[4] = {&lane0, &lane1, &lane2, &lane3};
142
143 // Determine default padding byte from first available lane
144 u8 default_padding = 0x00;
145 for (size_t i = 0; i < 4; i++) {
146 if (lanes[i]->has_value() && !(*lanes[i])->padding_frame.empty()) {
147 default_padding = (*lanes[i])->padding_frame[0];
148 break;
149 }
150 }
151
152 // Gather all bytes from each lane into temporary buffers
153 fl::vector<u8> lane_buffers[4];
154 const u8* lane_ptrs[4];
155
156 for (size_t lane = 0; lane < 4; lane++) {
157 lane_buffers[lane].resize(max_size);
158 lane_ptrs[lane] = lane_buffers[lane].data();
159
160 for (size_t byte_idx = 0; byte_idx < max_size; byte_idx++) {
161 lane_buffers[lane][byte_idx] = lanes[lane]->has_value() ?
162 getLaneByte(**lanes[lane], byte_idx, max_size) : default_padding;
163 }
164 }
165
166 // Perform transposition using ISR-safe primitive
167 transpose_4lane_inline(lane_ptrs, output.data(), max_size);
168
169 if (error) {
170 *error = nullptr; // Success, no error
171 }
172 return true;
173}
174
175// interleave_byte_4way removed - now uses transpose_4lane_inline() primitive
176
177// ----------------------------------------------------------------------------
178// 8-Way Transpose (Octal-SPI)
179// ----------------------------------------------------------------------------
180
182 fl::span<u8> output,
183 const char** error) FL_NO_EXCEPT {
184 // Validate output buffer size (must be divisible by 8)
185 if (output.size() % 8 != 0) {
186 if (error) {
187 *error = "Output buffer size must be divisible by 8";
188 }
189 return false;
190 }
191
192 // Calculate max lane size from output buffer
193 const size_t max_size = output.size() / 8;
194
195 // Handle empty case
196 if (max_size == 0) {
197 if (error) {
198 *error = nullptr; // No error, just empty
199 }
200 return true;
201 }
202
203 // Determine default padding byte from first available lane
204 u8 default_padding = 0x00;
205 for (size_t i = 0; i < 8; i++) {
206 if (lanes[i].has_value() && !lanes[i]->padding_frame.empty()) {
207 default_padding = lanes[i]->padding_frame[0];
208 break;
209 }
210 }
211
212 // Gather all bytes from each lane into temporary buffers
213 fl::vector<u8> lane_buffers[8];
214 const u8* lane_ptrs[8];
215
216 for (size_t lane = 0; lane < 8; lane++) {
217 lane_buffers[lane].resize(max_size);
218 lane_ptrs[lane] = lane_buffers[lane].data();
219
220 for (size_t byte_idx = 0; byte_idx < max_size; byte_idx++) {
221 lane_buffers[lane][byte_idx] = lanes[lane].has_value() ?
222 getLaneByte(*lanes[lane], byte_idx, max_size) : default_padding;
223 }
224 }
225
226 // Perform transposition using ISR-safe primitive
227 transpose_8lane_inline(lane_ptrs, output.data(), max_size);
228
229 if (error) {
230 *error = nullptr; // Success, no error
231 }
232 return true;
233}
234
235// interleave_byte_8way removed - now uses transpose_8lane_inline() primitive
236
237// ----------------------------------------------------------------------------
238// 16-Way Transpose (Hex-SPI)
239// ----------------------------------------------------------------------------
240
242 fl::span<u8> output,
243 const char** error) FL_NO_EXCEPT {
244 // Validate output buffer size (must be divisible by 16)
245 if (output.size() % 16 != 0) {
246 if (error) {
247 *error = "Output buffer size must be divisible by 16";
248 }
249 return false;
250 }
251
252 // Calculate max lane size from output buffer
253 const size_t max_size = output.size() / 16;
254
255 // Handle empty case
256 if (max_size == 0) {
257 if (error) {
258 *error = nullptr; // No error, just empty
259 }
260 return true;
261 }
262
263 // Determine default padding byte from first available lane
264 u8 default_padding = 0x00;
265 for (size_t i = 0; i < 16; i++) {
266 if (lanes[i].has_value() && !lanes[i]->padding_frame.empty()) {
267 default_padding = lanes[i]->padding_frame[0];
268 break;
269 }
270 }
271
272 // Gather all bytes from each lane into temporary buffers
273 fl::vector<u8> lane_buffers[16];
274 const u8* lane_ptrs[16];
275
276 for (size_t lane = 0; lane < 16; lane++) {
277 lane_buffers[lane].resize(max_size);
278 lane_ptrs[lane] = lane_buffers[lane].data();
279
280 for (size_t byte_idx = 0; byte_idx < max_size; byte_idx++) {
281 lane_buffers[lane][byte_idx] = lanes[lane].has_value() ?
282 getLaneByte(*lanes[lane], byte_idx, max_size) : default_padding;
283 }
284 }
285
286 // Perform transposition using ISR-safe primitive
287 transpose_16lane_inline(lane_ptrs, output.data(), max_size);
288
289 if (error) {
290 *error = nullptr; // Success, no error
291 }
292 return true;
293}
294
295// interleave_byte_16way removed - now uses transpose_16lane_inline() primitive
296
297// ----------------------------------------------------------------------------
298// Common Helper Functions
299// ----------------------------------------------------------------------------
300
301u8 SPITransposer::getLaneByte(const LaneData& lane, size_t byte_idx, size_t max_size) FL_NO_EXCEPT {
302 // Calculate padding needed for this lane
303 const size_t lane_size = lane.payload.size();
304 const size_t padding_bytes = max_size - lane_size;
305
306 // If we're in the padding region (prepended to beginning)
307 if (byte_idx < padding_bytes) {
308 // Return padding frame byte (repeating pattern)
309 if (!lane.padding_frame.empty()) {
310 return lane.padding_frame[byte_idx % lane.padding_frame.size()];
311 }
312 return 0x00; // Fallback to zero
313 }
314
315 // We're in the data region
316 const size_t data_idx = byte_idx - padding_bytes;
317 if (data_idx < lane_size) {
318 return lane.payload[data_idx];
319 }
320
321 // Should never reach here if max_size is correct
322 return 0x00;
323}
324
325} // namespace fl
326
bool empty() const FL_NO_EXCEPT
Definition optional.h:41
bool has_value() const FL_NO_EXCEPT
Definition optional.h:42
static bool transpose8(const fl::optional< LaneData > lanes[8], fl::span< u8 > output, const char **error=nullptr) FL_NO_EXCEPT
Transpose 8 lanes of data into interleaved octal-SPI format.
static bool transpose4(const fl::optional< LaneData > &lane0, const fl::optional< LaneData > &lane1, const fl::optional< LaneData > &lane2, const fl::optional< LaneData > &lane3, fl::span< u8 > output, const char **error=nullptr) FL_NO_EXCEPT
Transpose 4 lanes of data into interleaved quad-SPI format.
static bool transpose2(const fl::optional< LaneData > &lane0, const fl::optional< LaneData > &lane1, fl::span< u8 > output, const char **error=nullptr) FL_NO_EXCEPT
Transpose 2 lanes of data into interleaved dual-SPI format.
static bool transpose16(const fl::optional< LaneData > lanes[16], fl::span< u8 > output, const char **error=nullptr) FL_NO_EXCEPT
Transpose 16 lanes of data into interleaved hex-SPI format.
static u8 getLaneByte(const LaneData &lane, size_t byte_idx, size_t max_size) FL_NO_EXCEPT
Get byte from lane at given index, handling padding automatically.
Lane data structure: payload + padding frame.
void resize(fl::size n) FL_NO_EXCEPT
Definition vector.h:593
T * data() FL_NO_EXCEPT
Definition vector.h:619
#define FL_OPTIMIZATION_LEVEL_O3_BEGIN
#define FL_DISABLE_WARNING(warning)
#define FL_DISABLE_WARNING_PUSH
#define FL_OPTIMIZATION_LEVEL_O3_END
#define FL_DISABLE_WARNING_POP
#define FL_OPTIMIZE_FUNCTION
#define FL_IRAM
unsigned char u8
Definition stdint.h:131
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
Optional< T > optional
Definition optional.h:16
void transpose8x1_noinline(unsigned char *A, unsigned char *B) FL_NO_EXCEPT
Simplified 8x1 bit transpose (non-inline version)
void transpose_8lane_inline(const u8 *const lanes[8], u8 *output, size_t num_bytes) FL_NO_EXCEPT
Low-level bit-interleaving primitive for 8 lanes (ISR-safe)
void transpose_2lane_inline(const u8 *lane0_byte, const u8 *lane1_byte, u8 *output, size_t num_bytes) FL_NO_EXCEPT
Low-level bit-interleaving primitive for 2 lanes (ISR-safe)
void transpose_4lane_inline(const u8 *const lanes[4], u8 *output, size_t num_bytes) FL_NO_EXCEPT
Low-level bit-interleaving primitive for 4 lanes (ISR-safe)
void transpose_16lane_inline(const u8 *const lanes[16], u8 *output, size_t num_bytes) FL_NO_EXCEPT
Low-level bit-interleaving primitive for 16 lanes (ISR-safe)
Base definition for an LED controller.
Definition crgb.hpp:179
Unified bit transposition functions for FastLED.