FastLED 3.10.6
Loading...
Searching...
No Matches
fl::anonymous_namespace{uart_wave_encoder.cpp.hpp} Namespace Reference

Classes

struct  UartWaveFit
 Fit result for one UART wave geometry (P pulses per LED bit) More...
 

Functions

u32 absDiff (u32 a, u32 b) FL_NO_EXCEPT
 Absolute value of difference (unsigned-safe)
 
u8 buildUartByte (u8 pulses_a, u8 pulses_b, u8 pulses_per_bit=5) FL_NO_EXCEPT
 Build a UART data byte from two LED bit pulse counts.
 
u8 computePulseCount (u32 timing_ns, u32 pulse_width_ns) FL_NO_EXCEPT
 Compute HIGH pulse count for a given timing value.
 
UartWaveFit fitUartWave (const ChipsetTimingConfig &timing, u8 pulses_per_bit, u32 max_baud_rate) FL_NO_EXCEPT
 Evaluate whether P pulses/LED-bit can represent the timing.
 

total_err_ns cannot distinguish 125 ns spread over two symbols from 200 ns concentrated in one, and only the concentrated case leaves a symbol outside what a receiver will classify (FastLED#4379).

Pre-bump because the separation widening below is deliberate – it moves T1H away from T0H on purpose and a longer HIGH still reads as 1 – so charging it as error would relitigate #3569/#3572 rather than measure rounding.

bool ok u8 pulses_0 u8 pulses_1 u32 total_err_ns |T0H err| + |T1H err| vs nominal, post-bump

Function Documentation

◆ absDiff()

u32 fl::anonymous_namespace{uart_wave_encoder.cpp.hpp}::absDiff ( u32 a,
u32 b )

Absolute value of difference (unsigned-safe)

Definition at line 22 of file uart_wave_encoder.cpp.hpp.

22 {
23 return (a > b) ? (a - b) : (b - a);
24}

References absDiff(), and fl::FL_NO_EXCEPT.

Referenced by absDiff(), computePulseCount(), and fitUartWave().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ buildUartByte()

u8 fl::anonymous_namespace{uart_wave_encoder.cpp.hpp}::buildUartByte ( u8 pulses_a,
u8 pulses_b,
u8 pulses_per_bit = 5 )

Build a UART data byte from two LED bit pulse counts.

Constructs the 8-bit UART data value that, with TX inversion and LSB-first transmission, produces the desired wire waveform.

Wire model (10 bits with TX inversion): [START=H] [~D0] [~D1] [~D2] [~D3] [~D4] [~D5] [~D6] [~D7] [STOP=L]

LED bit A occupies positions 0-4 (START + D0..D3): START is always HIGH (1 pulse guaranteed) D0..D3 set HIGH for (pulses_a - 1) additional pulses, then LOW

LED bit B occupies positions 5-9 (D4..D7 + STOP): D4 must be HIGH for leading edge (1 pulse guaranteed) D5..D7 set HIGH for (pulses_b - 1) additional pulses, then LOW STOP is always LOW

Parameters
pulses_aHIGH pulse count for LED bit A [1, 4]
pulses_bHIGH pulse count for LED bit B [1, 4]
Returns
UART data byte value

Definition at line 66 of file uart_wave_encoder.cpp.hpp.

66 {
67 // Build the desired 2P-bit wire pattern (with TX inversion), where
68 // P = pulses_per_bit. Wire positions:
69 // [0=START] [1=~D0] ... [2P-2=~D(2P-3)] [2P-1=STOP]
70 //
71 // LED bit A (pos 0..P-1): pulses_a HIGH bits, then LOW
72 // LED bit B (pos P..2P-1): pulses_b HIGH bits, then LOW (STOP=L)
73
74 const int P = static_cast<int>(pulses_per_bit);
75 int wire[10]; // max frame = 10 (P=5)
76
77 // LED bit A: pulses_a HIGH bits starting from position 0
78 for (int i = 0; i < P; i++) {
79 wire[i] = (i < static_cast<int>(pulses_a)) ? 1 : 0;
80 }
81
82 // LED bit B: pulses_b HIGH bits starting from position P
83 for (int i = 0; i < P; i++) {
84 wire[P + i] = (i < static_cast<int>(pulses_b)) ? 1 : 0;
85 }
86
87 // Convert wire pattern back to UART data byte
88 // Wire[0] = START (inverted) = always H -> no control needed
89 // Wire[1..2P-2] = ~D0..~D(2P-3) (inverted data, LSB first)
90 // Wire[2P-1] = STOP (inverted) = always L -> no control needed
91 //
92 // So: wire[1+i] = ~D_i -> D_i = ~wire[1+i] = wire[1+i] ? 0 : 1
93 u8 byte_val = 0;
94 const int data_bits = 2 * P - 2;
95 for (int i = 0; i < data_bits; i++) {
96 int data_bit = wire[1 + i] ? 0 : 1; // Invert: wire HIGH -> data 0
97 byte_val |= static_cast<u8>(data_bit << i); // LSB first
98 }
99
100 return byte_val;
101}
Definition Luminova.h:46
unsigned char u8
Definition stdint.h:131
FASTLED_FORCE_INLINE fl::u8 P(fl::u8 x)

References buildUartByte(), fl::FL_NO_EXCEPT, and fl::P().

Referenced by buildUartByte().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ computePulseCount()

u8 fl::anonymous_namespace{uart_wave_encoder.cpp.hpp}::computePulseCount ( u32 timing_ns,
u32 pulse_width_ns )

Compute HIGH pulse count for a given timing value.

Parameters
timing_nsTiming value in nanoseconds (T0H or T1H)
pulse_width_nsWidth of each pulse in nanoseconds
Returns
Number of HIGH pulses (best-fit: floor or ceil, whichever is closer)

Tries both floor and ceil, picks the one with smaller error. Ties are broken by preferring the lower (floor) value, which keeps the HIGH time shorter – safer for most LED protocols. For example, WS2812 T1H=875ns at 250ns/pulse: floor=3 (750ns, err=125), ceil=4 (1000ns, err=125) -> picks 3 (shorter HIGH time).

Definition at line 36 of file uart_wave_encoder.cpp.hpp.

36 {
37 if (pulse_width_ns == 0) return 0;
38 u8 lo = static_cast<u8>(timing_ns / pulse_width_ns);
39 u8 hi = lo + 1;
40 u32 err_lo = absDiff(static_cast<u32>(lo) * pulse_width_ns, timing_ns);
41 u32 err_hi = absDiff(static_cast<u32>(hi) * pulse_width_ns, timing_ns);
42 // Prefer lo on tie (shorter HIGH time is safer)
43 return (err_hi < err_lo) ? hi : lo;
44}
u32 absDiff(u32 a, u32 b) FL_NO_EXCEPT
Absolute value of difference (unsigned-safe)
unsigned char u8
Definition stdint.h:131

References absDiff(), computePulseCount(), and fl::FL_NO_EXCEPT.

Referenced by computePulseCount(), and fitUartWave().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ fitUartWave()

UartWaveFit fl::anonymous_namespace{uart_wave_encoder.cpp.hpp}::fitUartWave ( const ChipsetTimingConfig & timing,
u8 pulses_per_bit,
u32 max_baud_rate )

Evaluate whether P pulses/LED-bit can represent the timing.

Shared by buildWave10Lut() and canRepresentTiming() so the LUT that gets built is always judged by the same rules that admitted it.

Definition at line 126 of file uart_wave_encoder.cpp.hpp.

128 {
129 UartWaveFit fit = {};
130 const u32 period_ns = timing.total_period_ns();
131 if (period_ns == 0) return fit;
132
133 // Baud = one UART bit per pulse = P / period. Must fit the ESP32
134 // UART ceiling.
135 const u64 baud =
136 static_cast<u64>(pulses_per_bit) * 1000000000ULL / period_ns;
137 if (baud == 0 || baud > max_baud_rate) return fit;
138
139 const u32 pulse_width_ns = period_ns / pulses_per_bit;
140 if (pulse_width_ns == 0) return fit;
141
142 const u32 t0h_ns = timing.t1_ns;
143 const u32 t1h_ns = timing.t1_ns + timing.t2_ns;
144 const u8 max_pulses = static_cast<u8>(pulses_per_bit - 1);
145
146 u8 pulses_0 = computePulseCount(t0h_ns, pulse_width_ns);
147 u8 pulses_1 = computePulseCount(t1h_ns, pulse_width_ns);
148 if (pulses_0 < 1 || pulses_0 > max_pulses) return fit;
149 if (pulses_1 < 1 || pulses_1 > max_pulses) return fit;
150 if (pulses_0 == pulses_1) return fit;
151
152 // FastLED#3569/#3572 -- enforce a minimum ABSOLUTE wire separation
153 // between the 0 and 1 symbols, widening T1H upward (a longer HIGH
154 // still reads as 1). Best-fit rounding alone can land them one
155 // pulse apart, and at high baud one pulse is too little: WS2812B-V5
156 // at P=5 rounds to 245 vs 490 ns -- inside real WS281x parts'
157 // sampling-threshold band (~550-625 ns); it flapped the WROOM bench
158 // decoder. Slow chipsets (WS2811-400: 500+ ns pulses) already
159 // exceed the floor with single-pulse separation.
160 constexpr u32 kMinSymbolSeparationNs = 400;
161 const u8 pulses_1_raw = pulses_1;
162 while (pulses_1 < max_pulses &&
163 static_cast<u32>(pulses_1 - pulses_0) * pulse_width_ns <
164 kMinSymbolSeparationNs) {
165 ++pulses_1;
166 }
167
168 // Quantized timing must be within half a pulse of nominal -- the
169 // natural error bound for best-fit rounding. When the separation
170 // bump deliberately widened T1H, allow exactly the pulses it added
171 // on top of that bound.
172 const u32 tolerance_ns = pulse_width_ns / 2;
173 const u32 actual_t0h = static_cast<u32>(pulses_0) * pulse_width_ns;
174 const u32 actual_t1h = static_cast<u32>(pulses_1) * pulse_width_ns;
175 const u32 t1h_tolerance_ns =
176 tolerance_ns +
177 static_cast<u32>(pulses_1 - pulses_1_raw) * pulse_width_ns;
178 const u32 err_t0h = absDiff(actual_t0h, t0h_ns);
179 const u32 err_t1h = absDiff(actual_t1h, t1h_ns);
180 if (err_t0h > tolerance_ns) return fit;
181 if (err_t1h > t1h_tolerance_ns) return fit;
182
183 const u32 raw_t1h = static_cast<u32>(pulses_1_raw) * pulse_width_ns;
184 const u32 err_t1h_raw = absDiff(raw_t1h, t1h_ns);
185
186 fit.ok = true;
187 fit.pulses_0 = pulses_0;
188 fit.pulses_1 = pulses_1;
189 fit.total_err_ns = err_t0h + err_t1h;
190 fit.max_err_ns = err_t0h > err_t1h_raw ? err_t0h : err_t1h_raw;
191 return fit;
192}
u8 computePulseCount(u32 timing_ns, u32 pulse_width_ns) FL_NO_EXCEPT
Compute HIGH pulse count for a given timing value.
u32 max_err_ns
Worst single-symbol error, measured pre-bump.
Fit result for one UART wave geometry (P pulses per LED bit)
fl::u64 u64
Definition stdint.h:220
u32 t1_ns
T0H: High time for bit 0 (nanoseconds)
u32 t2_ns
T1H-T0H: Additional high time for bit 1 (nanoseconds)
constexpr u32 total_period_ns() const FL_NO_EXCEPT
Get total bit period (T1 + T2 + T3)

References absDiff(), computePulseCount(), fitUartWave(), fl::FL_NO_EXCEPT, fl::anonymous_namespace{uart_wave_encoder.cpp.hpp}::UartWaveFit::max_err_ns, fl::anonymous_namespace{uart_wave_encoder.cpp.hpp}::UartWaveFit::ok, fl::anonymous_namespace{uart_wave_encoder.cpp.hpp}::UartWaveFit::pulses_0, fl::anonymous_namespace{uart_wave_encoder.cpp.hpp}::UartWaveFit::pulses_1, and fl::anonymous_namespace{uart_wave_encoder.cpp.hpp}::UartWaveFit::total_err_ns.

Referenced by fitUartWave().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: