FastLED 3.9.15
Loading...
Searching...
No Matches
apa102.h
Go to the documentation of this file.
1#pragma once
2
5#include "crgb.h"
6#include "eorder.h"
8#include "fastspi.h"
9// IWYU pragma: begin_keep
10#include "platforms/shared/spi_pixel_writer.h" // ok platform headers
11#include "fl/stl/noexcept.h"
12// IWYU pragma: end_keep
13
15//
16// APA102 definition - takes data/clock/select pin values (N.B. should take an SPI definition?)
17//
19
25template <
28 // APA102 has a bug where long strip can't handle full speed due to clock degredation.
29 // This only affects long strips, but then again if you have a short strip does 6 mhz actually slow
30 // you down? Probably not. And you can always bump it up for speed. Therefore we are prioritizing
31 // "just works" over "fastest possible" here.
32 // https://www.pjrc.com/why-apa102-leds-have-trouble-at-24-mhz/
33 fl::u32 SPI_SPEED = DATA_RATE_MHZ(6),
35 fl::u32 START_FRAME = 0x00000000,
36 fl::u32 END_FRAME = 0xFF000000
37>
38class APA102Controller : public CPixelLEDController<RGB_ORDER> {
39 typedef fl::SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
41
43 mSPI.writeWord(START_FRAME >> 16);
44 mSPI.writeWord(START_FRAME & 0xFFFF);
45 }
46 void endBoundary(int nLeds) {
47 int nDWords = (nLeds/32);
48 const fl::u8 b0 = fl::u8(END_FRAME >> 24 & 0x000000ff);
49 const fl::u8 b1 = fl::u8(END_FRAME >> 16 & 0x000000ff);
50 const fl::u8 b2 = fl::u8(END_FRAME >> 8 & 0x000000ff);
51 const fl::u8 b3 = fl::u8(END_FRAME >> 0 & 0x000000ff);
52 do {
53 mSPI.writeByte(b0);
54 mSPI.writeByte(b1);
55 mSPI.writeByte(b2);
56 mSPI.writeByte(b3);
57 } while(nDWords--);
58 }
59
61#ifdef FASTLED_SPI_BYTE_ONLY
62 mSPI.writeByte(0xE0 | brightness);
63 mSPI.writeByte(b0);
64 mSPI.writeByte(b1);
65 mSPI.writeByte(b2);
66#else
67 fl::u16 b = 0xE000 | (brightness << 8) | (fl::u16)b0;
68 mSPI.writeWord(b);
69 fl::u16 w = b1 << 8;
70 w |= b2;
71 mSPI.writeWord(w);
72#endif
73 }
74
76#ifdef FASTLED_SPI_BYTE_ONLY
77 mSPI.writeByte(b1);
78 mSPI.writeByte(b2);
79#else
80 mSPI.writeWord(fl::u16(b1) << 8 | b2);
81#endif
82 }
83
84public:
86
87 virtual void init() override {
88 mSPI.init();
89 }
90
91protected:
93 virtual void showPixels(PixelController<RGB_ORDER> & pixels) override {
94 switch (GAMMA_CORRECTION_MODE) {
96 showPixelsDefault(pixels);
97 break;
98 }
101 break;
102 }
103 }
104 }
105
106private:
107
110 fl::u8* out_s0, fl::u8* out_s1, fl::u8* out_s2, fl::u8* out_brightness) {
111#if FASTLED_HD_COLOR_MIXING
113 pixels.loadRGBScaleAndBrightness(out_s0, out_s1, out_s2, &brightness);
114 struct Math {
115 static fl::u16 map(fl::u16 x, fl::u16 in_min, fl::u16 in_max, fl::u16 out_min, fl::u16 out_max) { // okay static in header
116 const fl::u16 run = in_max - in_min;
117 const fl::u16 rise = out_max - out_min;
118 const fl::u16 delta = x - in_min;
119 return (delta * rise) / run + out_min;
120 }
121 };
122 // *out_brightness = Math::map(brightness, 0, 255, 0, 31);
123 fl::u16 bri = Math::map(brightness, 0, 255, 0, 31);
124 if (bri == 0 && brightness != 0) {
125 // Fixes https://github.com/FastLED/FastLED/issues/1908
126 bri = 1;
127 }
128 *out_brightness = static_cast<fl::u8>(bri);
129 return;
130#else
131 fl::u8 s0, s1, s2;
132 pixels.loadAndScaleRGB(&s0, &s1, &s2);
133#if FASTLED_USE_GLOBAL_BRIGHTNESS == 1
134 // This function is pure magic.
135 const fl::u16 maxBrightness = 0x1F;
136 // Use sequential comparisons to avoid nested fl::max (helps AVR register allocation)
137 fl::u8 max_s01 = (s0 > s1) ? s0 : s1;
138 fl::u8 max_component = (max_s01 > s2) ? max_s01 : s2;
139 fl::u16 brightness = ((((fl::u16)max_component + 1) * maxBrightness - 1) >> 8) + 1;
140 s0 = (maxBrightness * s0 + (brightness >> 1)) / brightness;
141 s1 = (maxBrightness * s1 + (brightness >> 1)) / brightness;
142 s2 = (maxBrightness * s2 + (brightness >> 1)) / brightness;
143#else
144 const fl::u8 brightness = 0x1F;
145#endif // FASTLED_USE_GLOBAL_BRIGHTNESS
146 *out_s0 = s0;
147 *out_s1 = s1;
148 *out_s2 = s2;
149 *out_brightness = static_cast<fl::u8>(brightness);
150#endif // FASTLED_HD_COLOR_MIXING
151 }
152
153 // Legacy showPixels implementation.
155 mSPI.select();
156 fl::u8 s0, s1, s2, global_brightness;
157 getGlobalBrightnessAndScalingFactors(pixels, &s0, &s1, &s2, &global_brightness);
159 while (pixels.has(1)) {
160 fl::u8 c0, c1, c2;
161 pixels.loadAndScaleRGB(&c0, &c1, &c2);
162 writeLed(global_brightness, c0, c1, c2);
163 pixels.stepDithering();
164 pixels.advanceData();
165 }
166 endBoundary(pixels.size());
167
168 mSPI.endTransaction();
169
170 // Finalize transmission (no-op on non-ESP32, flushes Quad-SPI on ESP32)
171 mSPI.finalizeTransmission();
172 }
173
175 static constexpr fl::u16 kBatchSize = 8;
176 const fl::u16 n = static_cast<fl::u16>(pixels.size());
177
178 // Extract uniform color scale and brightness (constant across all pixels)
179 fl::u8 scale_r, scale_g, scale_b, global_brightness;
180 #if FASTLED_HD_COLOR_MIXING
181 pixels.loadRGBScaleAndBrightness(&scale_r, &scale_g, &scale_b, &global_brightness);
182 #else
183 pixels.loadAndScaleRGB(&scale_r, &scale_g, &scale_b);
184 global_brightness = 255;
185 #endif
186 const CRGB colors_scale(scale_r, scale_g, scale_b);
187
188 // RGB reorder indices (compile-time constant from RGB_ORDER template param)
189 const fl::u8 b0_index = (static_cast<int>(RGB_ORDER) >> 6) & 0x3;
190 const fl::u8 b1_index = (static_cast<int>(RGB_ORDER) >> 3) & 0x3;
191 const fl::u8 b2_index = static_cast<int>(RGB_ORDER) & 0x3;
192
193 mSPI.select();
195
196 // Process pixels in batches of 8 using stack-allocated buffers
197 fl::u16 remaining = n;
198 while (remaining > 0) {
199 const fl::u16 batch = (remaining < kBatchSize) ? remaining : kBatchSize;
200 CRGB input_buf[kBatchSize];
201 fl::CRGBA5 gamma_buf[kBatchSize];
202
203 // Copy raw pixel bytes into CRGB input buffer
204 for (fl::u16 i = 0; i < batch; ++i) {
205 const fl::u8* raw = pixels.getRawPixelData();
206 input_buf[i] = CRGB(raw[0], raw[1], raw[2]);
207 pixels.advanceData();
208 }
209
211 fl::span<const CRGB>(input_buf, batch), colors_scale, global_brightness,
212 fl::span<fl::CRGBA5>(gamma_buf, batch));
213
214 for (fl::u16 i = 0; i < batch; ++i) {
215 const CRGB& rgb = gamma_buf[i].color;
216 writeLed(gamma_buf[i].brightness_5bit,
217 rgb.raw[b0_index], rgb.raw[b1_index], rgb.raw[b2_index]);
218 }
219
220 remaining -= batch;
221 }
222
223 endBoundary(n);
224 mSPI.endTransaction();
225 mSPI.finalizeTransmission();
226 }
227
228public:
233 static constexpr fl::u8 getPaddingByte() { return 0xFF; }
234
239 static fl::span<const fl::u8> getPaddingLEDFrame() { // okay static in header
240 // APA102 LED frame format: [111BBBBB][B][G][R]
241 // Black LED: 0xE0 (brightness=0), RGB=0,0,0
242 static const fl::u8 frame[] = { // okay static in header
243 0xE0, // Brightness byte (111 00000 = brightness 0)
244 0x00, // Blue = 0
245 0x00, // Green = 0
246 0x00 // Red = 0
247 };
248 return fl::span<const fl::u8>(frame, 4);
249 }
250
253 static constexpr size_t getPaddingLEDFrameSize() {
254 return 4;
255 }
256
261 static constexpr size_t calculateBytes(size_t num_leds) {
262 // APA102 protocol:
263 // - Start frame: 4 bytes (0x00000000)
264 // - LED data: 4 bytes per LED (brightness + RGB)
265 // - End frame: (num_leds / 32) + 1 DWords = 4 * ((num_leds / 32) + 1) bytes
266 return 4 + (num_leds * 4) + (4 * ((num_leds / 32) + 1));
267 }
268};
269
275template <
279 // APA102 has a bug where long strip can't handle full speed due to clock degredation.
280 // This only affects long strips, but then again if you have a short strip does 6 mhz actually slow
281 // you down? Probably not. And you can always bump it up for speed. Therefore we are prioritizing
282 // "just works" over "fastest possible" here.
283 // https://www.pjrc.com/why-apa102-leds-have-trouble-at-24-mhz/
284 fl::u32 SPI_SPEED = DATA_RATE_MHZ(6)
285>
287 DATA_PIN,
288 CLOCK_PIN,
289 RGB_ORDER,
290 SPI_SPEED,
291 fl::FiveBitGammaCorrectionMode::kFiveBitGammaCorrectionMode_BitShift,
292 fl::u32(0x00000000),
293 fl::u32(0x00000000)> {
294public:
297};
298
304template <
305 fl::u8 DATA_PIN,
306 fl::u8 CLOCK_PIN,
308 fl::u32 SPI_SPEED = DATA_RATE_MHZ(12)
309>
311 DATA_PIN,
312 CLOCK_PIN,
313 RGB_ORDER,
314 SPI_SPEED,
315 fl::FiveBitGammaCorrectionMode::kFiveBitGammaCorrectionMode_Null,
316 0x00000000,
317 0x00000000
318> {
319};
320
326template <
330 fl::u32 SPI_SPEED = DATA_RATE_MHZ(12)
331>
333 DATA_PIN,
334 CLOCK_PIN,
335 RGB_ORDER,
336 SPI_SPEED,
337 fl::FiveBitGammaCorrectionMode::kFiveBitGammaCorrectionMode_BitShift,
338 0x00000000,
339 0x00000000
340> {
341};
342
344template <
348 fl::u32 SPI_SPEED = DATA_RATE_MHZ(40)
349>
351 DATA_PIN,
352 CLOCK_PIN,
353 RGB_ORDER,
354 SPI_SPEED,
355 fl::FiveBitGammaCorrectionMode::kFiveBitGammaCorrectionMode_Null,
356 0x00000000,
357 0x00000000
358> {};
359
361template <
365 fl::u32 SPI_SPEED = DATA_RATE_MHZ(40)
366>
368 DATA_PIN,
369 CLOCK_PIN,
370 RGB_ORDER,
371 SPI_SPEED> {
372};
fl::UISlider brightness("Brightness", BRIGHTNESS, 0, 255)
int x
Definition simple.h:92
#define DATA_PIN
Definition ClientReal.h:82
#define RGB_ORDER
void showPixelsGammaBitShift(PixelController< RGB_ORDER > &pixels)
Definition apa102.h:174
virtual void init() override
Initialize the LED controller.
Definition apa102.h:87
static constexpr size_t calculateBytes(size_t num_leds)
Calculate total byte count for APA102 protocol Used for quad-SPI buffer pre-allocation.
Definition apa102.h:261
static constexpr size_t getPaddingLEDFrameSize()
Get size of padding LED frame in bytes.
Definition apa102.h:253
virtual void showPixels(PixelController< RGB_ORDER > &pixels) override
Send the LED data to the strip.
Definition apa102.h:93
static fl::span< const fl::u8 > getPaddingLEDFrame()
Get padding LED frame for synchronized latching in quad-SPI Returns a black LED frame to prepend to s...
Definition apa102.h:239
void showPixelsDefault(PixelController< RGB_ORDER > &pixels)
Definition apa102.h:154
FASTLED_FORCE_INLINE void writeLed(fl::u8 brightness, fl::u8 b0, fl::u8 b1, fl::u8 b2)
Definition apa102.h:60
fl::SPIOutput< DATA_PIN, CLOCK_PIN, SPI_SPEED > SPI
Definition apa102.h:39
static void getGlobalBrightnessAndScalingFactors(PixelController< RGB_ORDER > &pixels, fl::u8 *out_s0, fl::u8 *out_s1, fl::u8 *out_s2, fl::u8 *out_brightness)
Definition apa102.h:108
APA102Controller() FL_NOEXCEPT
Definition apa102.h:85
FASTLED_FORCE_INLINE void write2Bytes(fl::u8 b1, fl::u8 b2)
Definition apa102.h:75
void startBoundary()
Definition apa102.h:42
void endBoundary(int nLeds)
Definition apa102.h:46
static constexpr fl::u8 getPaddingByte()
Get the protocol-safe padding byte for APA102 Used for quad-SPI lane padding when strips have differe...
Definition apa102.h:233
APA102ControllerHD() FL_NOEXCEPT=default
CPixelLEDController(RegistrationMode mode)
HD107 is just the APA102 with a default 40Mhz clock rate.
Definition apa102.h:358
HD107HD is just the APA102HD with a default 40Mhz clock rate.
Definition apa102.h:371
SK9822 controller class.
Definition apa102.h:340
SK9822 controller class.
Definition apa102.h:318
defines the templated version of the CLEDController class
fl::EOrder EOrder
Definition eorder.h:12
constexpr EOrder RGB
Definition eorder.h:17
#define DATA_RATE_MHZ
Definition fastspi.h:95
Serial peripheral interface (SPI) definitions per platform.
Declares functions for five-bit gamma correction.
fl::CRGB CRGB
Definition crgb.h:25
unsigned char u8
Definition stdint.h:131
FiveBitGammaCorrectionMode
FL_OPTIMIZE_FUNCTION void five_bit_hd_gamma_bitshift(fl::span< const CRGB > colors, CRGB colors_scale, u8 global_brightness, fl::span< CRGB > out_colors, fl::span< u8 > out_power_5bit)
Base definition for an LED controller.
Definition crgb.hpp:179
#define FASTLED_FORCE_INLINE
#define FL_NOEXCEPT
FASTLED_FORCE_INLINE int size() const
Get the length of the LED strip.
FASTLED_FORCE_INLINE void loadAndScaleRGB(fl::u8 *b0_out, fl::u8 *b1_out, fl::u8 *b2_out)
const fl::u8 * getRawPixelData() const
Get read-only access to the current pixel data Used by encoders to access raw RGB values for HD proce...
FASTLED_FORCE_INLINE void advanceData()
Advance the data pointer forward, adjust position counter.
FASTLED_FORCE_INLINE bool has(int n)
Do we have n pixels left to process?
FASTLED_FORCE_INLINE void stepDithering()
Step the dithering forward - creates triangular wave that toggles between pixels.
Pixel controller class.