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