FastLED 3.10.8
Loading...
Searching...
No Matches
wave3.cpp.hpp
Go to the documentation of this file.
1
10
11#include "fl/channels/wave3.h"
14#include "fl/stl/isr/memcpy.h"
15#include "fl/stl/noexcept.h"
16
18
19namespace fl {
20
21// ============================================================================
22// Wave3 Eligibility Check
23// ============================================================================
24
27 const u32 period = timing.T1 + timing.T2 + timing.T3;
28 if (period == 0) {
29 return false;
30 }
31
32 // T0H = T1, T1H = T1 + T2
33 const float t0h_norm = static_cast<float>(timing.T1) / period;
34 const float t1h_norm = static_cast<float>(timing.T1 + timing.T2) / period;
35
36 // Round to nearest integer tick count out of 3
37 u32 ticks_bit0 = static_cast<u32>(t0h_norm * 3.0f + 0.5f);
38 u32 ticks_bit1 = static_cast<u32>(t1h_norm * 3.0f + 0.5f);
39
40 // Both must be in [1, 2] (need at least 1 high tick and 1 low tick)
41 if (ticks_bit0 < 1 || ticks_bit0 > 2) return false;
42 if (ticks_bit1 < 1 || ticks_bit1 > 2) return false;
43
44 // Must be different (one is 1-tick, other is 2-tick)
45 if (ticks_bit0 == ticks_bit1) return false;
46
47 return true;
48}
49
50// ============================================================================
51// Wave3 Clock Frequency
52// ============================================================================
53
56 const u32 period = timing.T1 + timing.T2 + timing.T3;
57 if (period == 0) {
58 return 0;
59 }
60 // clock_hz = 3,000,000,000 / total_period_ns
61 // Use u64 to avoid overflow
62 return static_cast<u32>(3000000000ULL / period);
63}
64
65// ============================================================================
66// LUT Builder from Timing Data
67// ============================================================================
68
70Wave3BitExpansionLut buildWave3ExpansionLUT(const ChipsetTiming& timing) FL_NO_EXCEPT {
71 Wave3BitExpansionLut lut;
72
73 const u32 period = timing.T1 + timing.T2 + timing.T3;
74
75 // T0H = T1, T1H = T1 + T2
76 const float t0h_norm = static_cast<float>(timing.T1) / period;
77 const float t1h_norm = static_cast<float>(timing.T1 + timing.T2) / period;
78
79 // Round to nearest integer tick count out of 3
80 u32 ticks_bit0 = static_cast<u32>(t0h_norm * 3.0f + 0.5f);
81 u32 ticks_bit1 = static_cast<u32>(t1h_norm * 3.0f + 0.5f);
82
83 // Clamp to valid range [0, 3]
84 if (ticks_bit0 > 3) ticks_bit0 = 3;
85 if (ticks_bit1 > 3) ticks_bit1 = 3;
86
87 // Generate 3-bit patterns for bit 0 and bit 1
88 // Pattern has ticks_bitN high bits followed by (3 - ticks_bitN) low bits
89 // MSB first within the 3-bit field
90 u8 pattern_bit0 = 0;
91 u8 pattern_bit1 = 0;
92
93 for (u32 i = 0; i < ticks_bit0; i++) {
94 pattern_bit0 |= (0x4 >> i); // Set bits from MSB: 0x4=100, 0x6=110, 0x7=111
95 }
96 for (u32 i = 0; i < ticks_bit1; i++) {
97 pattern_bit1 |= (0x4 >> i);
98 }
99
100 // Build LUT for all 16 nibbles
101 // Each nibble maps to 12 bits (4 LED bits × 3 ticks each)
102 for (u8 nibble = 0; nibble < 16; nibble++) {
103 u16 pattern = 0;
104 // Process bits MSB first (bit 3, 2, 1, 0)
105 for (int bit_pos = 3; bit_pos >= 0; bit_pos--) {
106 bool bit_set = (nibble >> bit_pos) & 1;
107 u8 tick_pattern = bit_set ? pattern_bit1 : pattern_bit0;
108 pattern = (pattern << 3) | (tick_pattern & 0x7);
109 }
110 lut.lut[nibble] = pattern;
111 }
112
113 return lut;
114}
115
116// ============================================================================
117// Public Transposition Functions
118// ============================================================================
119
121void wave3Transpose_2(const u8 (&FL_RESTRICT_PARAM lanes)[2],
122 const Wave3BitExpansionLut& lut,
123 u8 (&FL_RESTRICT_PARAM output)[2 * sizeof(Wave3Byte)]) FL_NO_EXCEPT {
124 Wave3Byte laneWaveforms[2];
125 detail::wave3_convert_byte_to_wave3byte(lanes[0], lut, &laneWaveforms[0]);
126 detail::wave3_convert_byte_to_wave3byte(lanes[1], lut, &laneWaveforms[1]);
127 detail::wave3_transpose_2(laneWaveforms, output);
128}
129
131void wave3Transpose_4(const u8 (&FL_RESTRICT_PARAM lanes)[4],
132 const Wave3BitExpansionLut& lut,
133 u8 (&FL_RESTRICT_PARAM output)[4 * sizeof(Wave3Byte)]) FL_NO_EXCEPT {
134 Wave3Byte laneWaveforms[4];
135 for (int lane = 0; lane < 4; lane++) {
136 detail::wave3_convert_byte_to_wave3byte(lanes[lane], lut, &laneWaveforms[lane]);
137 }
138 detail::wave3_transpose_4(laneWaveforms, output);
139}
140
142void wave3Transpose_8(const u8 (&FL_RESTRICT_PARAM lanes)[8],
143 const Wave3BitExpansionLut& lut,
144 u8 (&FL_RESTRICT_PARAM output)[8 * sizeof(Wave3Byte)]) FL_NO_EXCEPT {
145 Wave3Byte laneWaveforms[8];
146 for (int lane = 0; lane < 8; lane++) {
147 detail::wave3_convert_byte_to_wave3byte(lanes[lane], lut, &laneWaveforms[lane]);
148 }
149 detail::wave3_transpose_8(laneWaveforms, output);
150}
151
153void wave3Transpose_16(const u8 (&FL_RESTRICT_PARAM lanes)[16],
154 const Wave3BitExpansionLut& lut,
155 u8 (&FL_RESTRICT_PARAM output)[16 * sizeof(Wave3Byte)]) FL_NO_EXCEPT {
156 Wave3Byte laneWaveforms[16];
157 for (int lane = 0; lane < 16; lane++) {
158 detail::wave3_convert_byte_to_wave3byte(lanes[lane], lut, &laneWaveforms[lane]);
159 }
160 detail::wave3_transpose_16(laneWaveforms, output);
161}
162
163// ============================================================================
164// Untranspose Functions (Testing Only - Not Optimized)
165// ============================================================================
166
168void wave3Untranspose_2(const u8 (&FL_RESTRICT_PARAM transposed)[2 * sizeof(Wave3Byte)],
169 u8 (&FL_RESTRICT_PARAM output)[2 * sizeof(Wave3Byte)]) FL_NO_EXCEPT {
170 Wave3Byte lane_waves[2];
171
172 for (int symbol_idx = 0; symbol_idx < 3; symbol_idx++) {
173 u16 interleaved = ((u16)transposed[symbol_idx * 2] << 8) |
174 transposed[symbol_idx * 2 + 1];
175
176 u8 lane0_bits = 0;
177 u8 lane1_bits = 0;
178
179 for (int bit = 0; bit < 8; bit++) {
180 if (interleaved & (1 << (bit * 2 + 1))) {
181 lane0_bits |= (1 << bit);
182 }
183 if (interleaved & (1 << (bit * 2))) {
184 lane1_bits |= (1 << bit);
185 }
186 }
187
188 lane_waves[0].data[symbol_idx] = lane0_bits;
189 lane_waves[1].data[symbol_idx] = lane1_bits;
190 }
191
192 fl::isr::memcopy(output, &lane_waves[0], sizeof(Wave3Byte));
193 fl::isr::memcopy(output + sizeof(Wave3Byte), &lane_waves[1], sizeof(Wave3Byte));
194}
195
197void wave3Untranspose_4(const u8 (&FL_RESTRICT_PARAM transposed)[4 * sizeof(Wave3Byte)],
198 u8 (&FL_RESTRICT_PARAM output)[4 * sizeof(Wave3Byte)]) FL_NO_EXCEPT {
199 Wave3Byte lane_waves[4];
200
201 for (int symbol_idx = 0; symbol_idx < 3; symbol_idx++) {
202 u8 lane_bytes[4] = {0, 0, 0, 0};
203
204 for (int byte_idx = 0; byte_idx < 4; byte_idx++) {
205 u8 input_byte = transposed[symbol_idx * 4 + byte_idx];
206
207 int pulse_bit_hi = 7 - (byte_idx * 2);
208 int pulse_bit_lo = pulse_bit_hi - 1;
209
210 for (int lane = 0; lane < 4; lane++) {
211 u8 pulse_hi = (input_byte >> (4 + lane)) & 1;
212 u8 pulse_lo = (input_byte >> lane) & 1;
213 lane_bytes[lane] |= (pulse_hi << pulse_bit_hi);
214 lane_bytes[lane] |= (pulse_lo << pulse_bit_lo);
215 }
216 }
217
218 for (int lane = 0; lane < 4; lane++) {
219 lane_waves[lane].data[symbol_idx] = lane_bytes[lane];
220 }
221 }
222
223 for (int lane = 0; lane < 4; lane++) {
224 fl::isr::memcopy(output + lane * sizeof(Wave3Byte), &lane_waves[lane], sizeof(Wave3Byte));
225 }
226}
227
229void wave3Untranspose_8(const u8 (&FL_RESTRICT_PARAM transposed)[8 * sizeof(Wave3Byte)],
230 u8 (&FL_RESTRICT_PARAM output)[8 * sizeof(Wave3Byte)]) FL_NO_EXCEPT {
231 Wave3Byte lane_waves[8];
232
233 for (int symbol_idx = 0; symbol_idx < 3; symbol_idx++) {
234 u8 lane_bytes[8] = {0, 0, 0, 0, 0, 0, 0, 0};
235
236 // After transpose+reversal: byte 0 = bit 7 (first pulse, MSB),
237 // byte 7 = bit 0 (last pulse, LSB)
238 for (int byte_idx = 0; byte_idx < 8; byte_idx++) {
239 u8 input_byte = transposed[symbol_idx * 8 + byte_idx];
240 // byte 0 = bit 7, byte 7 = bit 0 (reversed order)
241 int pulse_bit = 7 - byte_idx;
242
243 for (int lane = 0; lane < 8; lane++) {
244 u8 pulse = (input_byte >> lane) & 1;
245 lane_bytes[lane] |= (pulse << pulse_bit);
246 }
247 }
248
249 for (int lane = 0; lane < 8; lane++) {
250 lane_waves[lane].data[symbol_idx] = lane_bytes[lane];
251 }
252 }
253
254 for (int lane = 0; lane < 8; lane++) {
255 fl::isr::memcopy(output + lane * sizeof(Wave3Byte), &lane_waves[lane], sizeof(Wave3Byte));
256 }
257}
258
260void wave3Untranspose_16(const u8 (&FL_RESTRICT_PARAM transposed)[16 * sizeof(Wave3Byte)],
261 u8 (&FL_RESTRICT_PARAM output)[16 * sizeof(Wave3Byte)]) FL_NO_EXCEPT {
262 Wave3Byte lane_waves[16];
263
264 for (int symbol_idx = 0; symbol_idx < 3; symbol_idx++) {
265 u8 lane_bytes[16] = {0};
266
267 for (int pulse_idx = 0; pulse_idx < 8; pulse_idx++) {
268 int pulse_bit = 7 - pulse_idx;
269 int input_offset = symbol_idx * 16 + pulse_idx * 2;
270 u16 input_word = (u16)transposed[input_offset] |
271 ((u16)transposed[input_offset + 1] << 8);
272
273 for (int lane = 0; lane < 16; lane++) {
274 u8 pulse = (input_word >> lane) & 1;
275 lane_bytes[lane] |= (pulse << pulse_bit);
276 }
277 }
278
279 for (int lane = 0; lane < 16; lane++) {
280 lane_waves[lane].data[symbol_idx] = lane_bytes[lane];
281 }
282 }
283
284 for (int lane = 0; lane < 16; lane++) {
285 fl::isr::memcopy(output + lane * sizeof(Wave3Byte), &lane_waves[lane], sizeof(Wave3Byte));
286 }
287}
288
289} // namespace fl
290
#define FL_OPTIMIZATION_LEVEL_O3_BEGIN
#define FL_OPTIMIZATION_LEVEL_O3_END
#define FL_OPTIMIZE_FUNCTION
#define FL_IRAM
#define FL_RESTRICT_PARAM
Out-of-line declarations for wave3 transposition internals.
Centralized LED chipset timing definitions with nanosecond precision.
ISR-safe memory operations (inline, header-only)
FL_IRAM FL_OPTIMIZE_FUNCTION void wave3_transpose_4(const Wave3Byte lane_waves[4], u8 output[4 *sizeof(Wave3Byte)]) FL_NO_EXCEPT
Transpose 4 lanes of Wave3Byte data.
Definition wave3.cpp.hpp:55
FL_IRAM FL_OPTIMIZE_FUNCTION void wave3_convert_byte_to_wave3byte(u8 byte_value, const Wave3BitExpansionLut &lut, Wave3Byte *output) FL_NO_EXCEPT
Convert one byte to a Wave3Byte using the nibble LUT.
Definition wave3.cpp.hpp:23
FL_IRAM FL_OPTIMIZE_FUNCTION void wave3_transpose_2(const Wave3Byte lane_waves[2], u8 output[2 *sizeof(Wave3Byte)]) FL_NO_EXCEPT
Transpose 2 lanes of Wave3Byte data into interleaved format.
Definition wave3.cpp.hpp:35
FL_IRAM FL_OPTIMIZE_FUNCTION void wave3_transpose_8(const Wave3Byte lane_waves[8], u8 output[8 *sizeof(Wave3Byte)]) FL_NO_EXCEPT
Transpose 8 lanes of Wave3Byte data via the spread-LUT path (#2533).
FL_IRAM FL_OPTIMIZE_FUNCTION void wave3_transpose_16(const Wave3Byte lane_waves[16], u8 output[16 *sizeof(Wave3Byte)]) FL_NO_EXCEPT
Transpose 16 lanes of Wave3Byte data via the spread-LUT path (#2533).
FL_OPTIMIZE_FUNCTION FL_IRAM FASTLED_FORCE_INLINE void memcopy(void *FL_RESTRICT_PARAM dst, const void *FL_RESTRICT_PARAM src, size_t num_bytes)
ISR-optimized memcpy with alignment detection and switch dispatch.
Definition memcpy.h:75
FL_OPTIMIZE_FUNCTION FL_IRAM void wave3Transpose_2(const u8(&FL_RESTRICT_PARAM lanes)[2], const Wave3BitExpansionLut &lut, u8(&FL_RESTRICT_PARAM output)[2 *sizeof(Wave3Byte)]) FL_NO_EXCEPT
unsigned char u8
Definition stdint.h:131
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
FL_OPTIMIZE_FUNCTION void wave3Untranspose_2(const u8(&FL_RESTRICT_PARAM transposed)[2 *sizeof(Wave3Byte)], u8(&FL_RESTRICT_PARAM output)[2 *sizeof(Wave3Byte)]) FL_NO_EXCEPT
FL_OPTIMIZE_FUNCTION Wave3BitExpansionLut buildWave3ExpansionLUT(const ChipsetTiming &timing) FL_NO_EXCEPT
Build a Wave3BitExpansionLut from chipset timing data.
Definition wave3.cpp.hpp:70
FL_OPTIMIZE_FUNCTION u32 wave3ClockFrequencyHz(const ChipsetTiming &timing) FL_NO_EXCEPT
Calculate the clock frequency for wave3 encoding.
Definition wave3.cpp.hpp:55
FL_OPTIMIZE_FUNCTION FL_IRAM void wave3Transpose_16(const u8(&FL_RESTRICT_PARAM lanes)[16], const Wave3BitExpansionLut &lut, u8(&FL_RESTRICT_PARAM output)[16 *sizeof(Wave3Byte)]) FL_NO_EXCEPT
FL_OPTIMIZE_FUNCTION FL_IRAM void wave3Transpose_8(const u8(&FL_RESTRICT_PARAM lanes)[8], const Wave3BitExpansionLut &lut, u8(&FL_RESTRICT_PARAM output)[8 *sizeof(Wave3Byte)]) FL_NO_EXCEPT
FL_OPTIMIZE_FUNCTION void wave3Untranspose_4(const u8(&FL_RESTRICT_PARAM transposed)[4 *sizeof(Wave3Byte)], u8(&FL_RESTRICT_PARAM output)[4 *sizeof(Wave3Byte)]) FL_NO_EXCEPT
FL_OPTIMIZE_FUNCTION FL_IRAM void wave3Transpose_4(const u8(&FL_RESTRICT_PARAM lanes)[4], const Wave3BitExpansionLut &lut, u8(&FL_RESTRICT_PARAM output)[4 *sizeof(Wave3Byte)]) FL_NO_EXCEPT
FL_OPTIMIZE_FUNCTION void wave3Untranspose_16(const u8(&FL_RESTRICT_PARAM transposed)[16 *sizeof(Wave3Byte)], u8(&FL_RESTRICT_PARAM output)[16 *sizeof(Wave3Byte)]) FL_NO_EXCEPT
FL_OPTIMIZE_FUNCTION bool canUseWave3(const ChipsetTiming &timing) FL_NO_EXCEPT
Check if a chipset timing is eligible for wave3 encoding.
Definition wave3.cpp.hpp:26
FL_OPTIMIZE_FUNCTION void wave3Untranspose_8(const u8(&FL_RESTRICT_PARAM transposed)[8 *sizeof(Wave3Byte)], u8(&FL_RESTRICT_PARAM output)[8 *sizeof(Wave3Byte)]) FL_NO_EXCEPT
Base definition for an LED controller.
Definition crgb.hpp:179
u8 data[3]
Definition wave3.h:22
Type-safe container for 3-byte wave pulse pattern (wave3 encoding)
Definition wave3.h:21
Generic chipset timing entry Provides T1, T2, T3 timing parameters in nanoseconds for any LED protoco...
Definition led_timing.h:86