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