Uh oh!
There was an error while loading. Please reload this page.
forked from FastLED/FastLED
- Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcontroller.h
More file actions
Latest commit
418 lines (347 loc) · 20 KB
/
Copy pathcontroller.h
File metadata and controls
418 lines (347 loc) · 20 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
#ifndef __INC_CONTROLLER_H
#define__INC_CONTROLLER_H
///@file controller.h
/// base definitions used by led controllers for writing out led data
#include"FastLED.h"
#include"led_sysdefs.h"
#include"pixeltypes.h"
#include"color.h"
#include<stddef.h>
FASTLED_NAMESPACE_BEGIN
#defineRO(X) RGB_BYTE(RGB_ORDER, X)
#defineRGB_BYTE(RO,X) (((RO)>>(3*(2-(X)))) & 0x3)
#defineRGB_BYTE0(RO) ((RO>>6) & 0x3)
#defineRGB_BYTE1(RO) ((RO>>3) & 0x3)
#defineRGB_BYTE2(RO) ((RO) & 0x3)
// operator byte *(struct CRGB[] arr) { return (byte*)arr; }
#defineDISABLE_DITHER0x00
#defineBINARY_DITHER0x01
typedefuint8_t EDitherMode;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// LED Controller interface definition
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Base definition for an LED controller. Pretty much the methods that every LED controller object will make available.
/// Note that the showARGB method is not impelemented for all controllers yet. Note also the methods for eventual checking
/// of background writing of data (I'm looking at you, teensy 3.0 DMA controller!). If you want to pass LED controllers around
/// to methods, make them references to this type, keeps your code saner. However, most people won't be seeing/using these objects
/// directly at all
classCLEDController {
protected:
friendclassCFastLED;
CRGB *m_Data;
CLEDController *m_pNext;
CRGB m_ColorCorrection;
CRGB m_ColorTemperature;
EDitherMode m_DitherMode;
int m_nLeds;
static CLEDController *m_pHead;
static CLEDController *m_pTail;
/// set all the leds on the controller to a given color
///@param data the crgb color to set the leds to
///@param nLeds the numner of leds to set to this color
///@param scale the rgb scaling value for outputting color
virtualvoidshowColor(conststructCRGB & data, int nLeds, CRGB scale) = 0;
/// write the passed in rgb data out to the leds managed by this controller
///@param data the rgb data to write out to the strip
///@param nLeds the number of leds being written out
///@param scale the rgb scaling to apply to each led before writing it out
virtualvoidshow(conststructCRGB *data, int nLeds, CRGB scale) = 0;
public:
/// create an led controller object, add it to the chain of controllers
CLEDController() : m_Data(NULL), m_ColorCorrection(UncorrectedColor), m_ColorTemperature(UncorrectedTemperature), m_DitherMode(BINARY_DITHER), m_nLeds(0) {
m_pNext = NULL;
if(m_pHead==NULL) { m_pHead = this; }
if(m_pTail != NULL) { m_pTail->m_pNext = this; }
m_pTail = this;
}
///initialize the LED controller
virtualvoidinit() = 0;
///clear out/zero out the given number of leds.
virtualvoidclearLeds(int nLeds) { showColor(CRGB::Black, nLeds, CRGB::Black); }
/// show function w/integer brightness, will scale for color correction and temperature
voidshow(conststructCRGB *data, int nLeds, uint8_t brightness) {
show(data, nLeds, getAdjustment(brightness));
}
/// show function w/integer brightness, will scale for color correction and temperature
voidshowColor(conststructCRGB &data, int nLeds, uint8_t brightness) {
showColor(data, nLeds, getAdjustment(brightness));
}
/// show function using the "attached to this controller" led data
voidshowLeds(uint8_t brightness=255) {
show(m_Data, m_nLeds, getAdjustment(brightness));
}
/// show the given color on the led strip
voidshowColor(conststructCRGB & data, uint8_t brightness=255) {
showColor(data, m_nLeds, getAdjustment(brightness));
}
/// get the first led controller in the chain of controllers
static CLEDController *head() { return m_pHead; }
/// get the next controller in the chain after this one. will return NULL at the end of the chain
CLEDController *next() { return m_pNext; }
/// set the default array of leds to be used by this controller
CLEDController & setLeds(CRGB *data, int nLeds) {
m_Data = data;
m_nLeds = nLeds;
return *this;
}
/// zero out the led data managed by this controller
voidclearLedData() {
if(m_Data) {
memset8((void*)m_Data, 0, sizeof(structCRGB) * m_nLeds);
}
}
/// How many leds does this controller manage?
virtualintsize() { return m_nLeds; }
/// Pointer to the CRGB array for this controller
CRGB* leds() { return m_Data; }
/// Reference to the n'th item in the controller
CRGB &operator[](int x) { return m_Data[x]; }
/// set the dithering mode for this controller to use
inline CLEDController & setDither(uint8_t ditherMode = BINARY_DITHER) { m_DitherMode = ditherMode; return *this; }
/// get the dithering option currently set for this controller
inlineuint8_tgetDither() { return m_DitherMode; }
/// the the color corrction to use for this controller, expressed as an rgb object
CLEDController & setCorrection(CRGB correction) { m_ColorCorrection = correction; return *this; }
/// set the color correction to use for this controller
CLEDController & setCorrection(LEDColorCorrection correction) { m_ColorCorrection = correction; return *this; }
/// get the correction value used by this controller
CRGBgetCorrection() { return m_ColorCorrection; }
/// set the color temperature, aka white point, for this controller
CLEDController & setTemperature(CRGB temperature) { m_ColorTemperature = temperature; return *this; }
/// set the color temperature, aka white point, for this controller
CLEDController & setTemperature(ColorTemperature temperature) { m_ColorTemperature = temperature; return *this; }
/// get the color temperature, aka whipe point, for this controller
CRGBgetTemperature() { return m_ColorTemperature; }
/// Get the combined brightness/color adjustment for this controller
CRGBgetAdjustment(uint8_t scale) {
returncomputeAdjustment(scale, m_ColorCorrection, m_ColorTemperature);
}
staticCRGBcomputeAdjustment(uint8_t scale, constCRGB & colorCorrection, constCRGB & colorTemperature) {
#if defined(NO_CORRECTION) && (NO_CORRECTION==1)
returnCRGB(scale,scale,scale);
#else
CRGBadj(0,0,0);
if(scale > 0) {
for(uint8_t i = 0; i < 3; i++) {
uint8_t cc = colorCorrection.raw[i];
uint8_t ct = colorTemperature.raw[i];
if(cc > 0 && ct > 0) {
uint32_t work = (((uint32_t)cc)+1) * (((uint32_t)ct)+1) * scale;
work /= 0x10000L;
adj.raw[i] = work & 0xFF;
}
}
}
return adj;
#endif
}
virtualuint16_tgetMaxRefreshRate() const { return0; }
};
// Pixel controller class. This is the class that we use to centralize pixel access in a block of data, including
// support for things like RGB reordering, scaling, dithering, skipping (for ARGB data), and eventually, we will
// centralize 8/12/16 conversions here as well.
template<EOrder RGB_ORDER, intLANES=1, uint32_tMASK=0xFFFFFFFF>
structPixelController {
constuint8_t *mData;
intmLen,mLenRemaining;
uint8_t d[3];
uint8_t e[3];
CRGBmScale;
int8_tmAdvance;
intmOffsets[LANES];
PixelController(const PixelController & other) {
d[0] = other.d[0];
d[1] = other.d[1];
d[2] = other.d[2];
e[0] = other.e[0];
e[1] = other.e[1];
e[2] = other.e[2];
mData = other.mData;
mScale = other.mScale;
mAdvance = other.mAdvance;
mLenRemaining = mLen = other.mLen;
for(int i = 0; i < LANES; i++) { mOffsets[i] = other.mOffsets[i]; }
}
voidinitOffsets(int len) {
int nOffset = 0;
for(int i = 0; i < LANES; i++) {
mOffsets[i] = nOffset;
if((1<<i) & MASK) { nOffset += (len * mAdvance); }
}
}
PixelController(constuint8_t *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER, bool advance=true, uint8_t skip=0) : mData(d), mLen(len), mLenRemaining(len), mScale(s) {
enable_dithering(dither);
mData += skip;
mAdvance = (advance) ? 3+skip : 0;
initOffsets(len);
}
PixelController(constCRGB *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((constuint8_t*)d), mLen(len), mLenRemaining(len), mScale(s) {
enable_dithering(dither);
mAdvance = 3;
initOffsets(len);
}
PixelController(constCRGB &d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((constuint8_t*)&d), mLen(len), mLenRemaining(len), mScale(s) {
enable_dithering(dither);
mAdvance = 0;
initOffsets(len);
}
voidinit_binary_dithering() {
#if !defined(NO_DITHERING) || (NO_DITHERING != 1)
// Set 'virtual bits' of dithering to the highest level
// that is not likely to cause excessive flickering at
// low brightness levels + low update rates.
// These pre-set values are a little ambitious, since
// a 400Hz update rate for WS2811-family LEDs is only
// possible with 85 pixels or fewer.
// Once we have a 'number of milliseconds since last update'
// value available here, we can quickly calculate the correct
// number of 'virtual bits' on the fly with a couple of 'if'
// statements -- no division required. At this point,
// the division is done at compile time, so there's no runtime
// cost, but the values are still hard-coded.
#defineMAX_LIKELY_UPDATE_RATE_HZ400
#defineMIN_ACCEPTABLE_DITHER_RATE_HZ50
#defineUPDATES_PER_FULL_DITHER_CYCLE (MAX_LIKELY_UPDATE_RATE_HZ / MIN_ACCEPTABLE_DITHER_RATE_HZ)
#defineRECOMMENDED_VIRTUAL_BITS ((UPDATES_PER_FULL_DITHER_CYCLE>1) + \
(UPDATES_PER_FULL_DITHER_CYCLE>2) + \
(UPDATES_PER_FULL_DITHER_CYCLE>4) + \
(UPDATES_PER_FULL_DITHER_CYCLE>8) + \
(UPDATES_PER_FULL_DITHER_CYCLE>16) + \
(UPDATES_PER_FULL_DITHER_CYCLE>32) + \
(UPDATES_PER_FULL_DITHER_CYCLE>64) + \
(UPDATES_PER_FULL_DITHER_CYCLE>128) )
#defineVIRTUAL_BITSRECOMMENDED_VIRTUAL_BITS
// R is the digther signal 'counter'.
staticuint8_t R = 0;
R++;
// R is wrapped around at 2^ditherBits,
// so if ditherBits is 2, R will cycle through (0,1,2,3)
uint8_t ditherBits = VIRTUAL_BITS;
R &= (0x01 << ditherBits) - 1;
// Q is the "unscaled dither signal" itself.
// It's initialized to the reversed bits of R.
// If 'ditherBits' is 2, Q here will cycle through (0,128,64,192)
uint8_t Q = 0;
// Reverse bits in a byte
{
if(R & 0x01) { Q |= 0x80; }
if(R & 0x02) { Q |= 0x40; }
if(R & 0x04) { Q |= 0x20; }
if(R & 0x08) { Q |= 0x10; }
if(R & 0x10) { Q |= 0x08; }
if(R & 0x20) { Q |= 0x04; }
if(R & 0x40) { Q |= 0x02; }
if(R & 0x80) { Q |= 0x01; }
}
// Now we adjust Q to fall in the center of each range,
// instead of at the start of the range.
// If ditherBits is 2, Q will be (0, 128, 64, 192) at first,
// and this adjustment makes it (31, 159, 95, 223).
if( ditherBits < 8) {
Q += 0x01 << (7 - ditherBits);
}
// D and E form the "scaled dither signal"
// which is added to pixel values to affect the
// actual dithering.
// Setup the initial D and E values
for(int i = 0; i < 3; i++) {
uint8_t s = mScale.raw[i];
e[i] = s ? (256/s) + 1 : 0;
d[i] = scale8(Q, e[i]);
#if (FASTLED_SCALE8_FIXED == 1)
if(d[i]) (d[i]--);
#endif
if(e[i]) e[i]--;
}
#endif
}
// Do we have n pixels left to process?
__attribute__((always_inline)) inlineboolhas(int n) {
returnmLenRemaining >= n;
}
// toggle dithering enable
voidenable_dithering(EDitherMode dither) {
switch(dither) {
caseBINARY_DITHER: init_binary_dithering(); break;
default: d[0]=d[1]=d[2]=e[0]=e[1]=e[2]=0; break;
}
}
__attribute__((always_inline)) inlineintsize() { returnmLen; }
// get the amount to advance the pointer by
__attribute__((always_inline)) inlineintadvanceBy() { returnmAdvance; }
// advance the data pointer forward, adjust position counter
__attribute__((always_inline)) inlinevoidadvanceData() { mData += mAdvance; mLenRemaining--;}
// step the dithering forward
__attribute__((always_inline)) inlinevoidstepDithering() {
// IF UPDATING HERE, BE SURE TO UPDATE THE ASM VERSION IN
// clockless_trinket.h!
d[0] = e[0] - d[0];
d[1] = e[1] - d[1];
d[2] = e[2] - d[2];
}
// Some chipsets pre-cycle the first byte, which means we want to cycle byte 0's dithering separately
__attribute__((always_inline)) inlinevoidpreStepFirstByteDithering() {
d[RO(0)] = e[RO(0)] - d[RO(0)];
}
template<intSLOT> __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc) { return pc.mData[RO(SLOT)]; }
template<intSLOT> __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc, int lane) { return pc.mData[pc.mOffsets[lane] + RO(SLOT)]; }
template<intSLOT> __attribute__((always_inline)) inline static uint8_t dither(PixelController & pc, uint8_t b) { return b ? qadd8(b, pc.d[RO(SLOT)]) : 0; }
template<intSLOT> __attribute__((always_inline)) inline static uint8_t dither(PixelController & , uint8_t b, uint8_t d) { return b ? qadd8(b,d) : 0; }
template<intSLOT> __attribute__((always_inline)) inline static uint8_t scale(PixelController & pc, uint8_t b) { returnscale8(b, pc.mScale.raw[RO(SLOT)]); }
template<intSLOT> __attribute__((always_inline)) inline static uint8_t scale(PixelController & , uint8_t b, uint8_t scale) { returnscale8(b, scale); }
// composite shortcut functions for loading, dithering, and scaling
template<intSLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc) { return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc))); }
template<intSLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane) { return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc, lane))); }
template<intSLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t d, uint8_t scale) { returnscale8(pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc, lane), d), scale); }
template<intSLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t scale) { returnscale8(pc.loadByte<SLOT>(pc, lane), scale); }
template<intSLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc); }
template<intSLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc, lane); }
template<intSLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane, uint8_t scale) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc, lane, scale); }
template<intSLOT> __attribute__((always_inline)) inline static uint8_t getd(PixelController & pc) { return pc.d[RO(SLOT)]; }
template<intSLOT> __attribute__((always_inline)) inline static uint8_t getscale(PixelController & pc) { return pc.mScale.raw[RO(SLOT)]; }
// Helper functions to get around gcc stupidities
__attribute__((always_inline)) inlineuint8_tloadAndScale0(int lane, uint8_t scale) { return loadAndScale<0>(*this, lane, scale); }
__attribute__((always_inline)) inlineuint8_tloadAndScale1(int lane, uint8_t scale) { return loadAndScale<1>(*this, lane, scale); }
__attribute__((always_inline)) inlineuint8_tloadAndScale2(int lane, uint8_t scale) { return loadAndScale<2>(*this, lane, scale); }
__attribute__((always_inline)) inlineuint8_tadvanceAndLoadAndScale0(int lane, uint8_t scale) { return advanceAndLoadAndScale<0>(*this, lane, scale); }
__attribute__((always_inline)) inlineuint8_tstepAdvanceAndLoadAndScale0(int lane, uint8_t scale) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane, scale); }
__attribute__((always_inline)) inlineuint8_tloadAndScale0(int lane) { return loadAndScale<0>(*this, lane); }
__attribute__((always_inline)) inlineuint8_tloadAndScale1(int lane) { return loadAndScale<1>(*this, lane); }
__attribute__((always_inline)) inlineuint8_tloadAndScale2(int lane) { return loadAndScale<2>(*this, lane); }
__attribute__((always_inline)) inlineuint8_tadvanceAndLoadAndScale0(int lane) { return advanceAndLoadAndScale<0>(*this, lane); }
__attribute__((always_inline)) inlineuint8_tstepAdvanceAndLoadAndScale0(int lane) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane); }
__attribute__((always_inline)) inlineuint8_tloadAndScale0() { return loadAndScale<0>(*this); }
__attribute__((always_inline)) inlineuint8_tloadAndScale1() { return loadAndScale<1>(*this); }
__attribute__((always_inline)) inlineuint8_tloadAndScale2() { return loadAndScale<2>(*this); }
__attribute__((always_inline)) inlineuint8_tadvanceAndLoadAndScale0() { return advanceAndLoadAndScale<0>(*this); }
__attribute__((always_inline)) inlineuint8_tstepAdvanceAndLoadAndScale0() { stepDithering(); return advanceAndLoadAndScale<0>(*this); }
__attribute__((always_inline)) inlineuint8_tgetScale0() { return getscale<0>(*this); }
__attribute__((always_inline)) inlineuint8_tgetScale1() { return getscale<1>(*this); }
__attribute__((always_inline)) inlineuint8_tgetScale2() { return getscale<2>(*this); }
};
template<EOrder RGB_ORDER, intLANES=1, uint32_tMASK=0xFFFFFFFF> classCPixelLEDController : publicCLEDController {
protected:
virtualvoidshowPixels(PixelController<RGB_ORDER,LANES,MASK> & pixels) = 0;
/// set all the leds on the controller to a given color
///@param data the crgb color to set the leds to
///@param nLeds the numner of leds to set to this color
///@param scale the rgb scaling value for outputting color
virtualvoidshowColor(conststructCRGB & data, int nLeds, CRGB scale) {
PixelController<RGB_ORDER, LANES, MASK> pixels(data, nLeds, scale, getDither());
showPixels(pixels);
}
/// write the passed in rgb data out to the leds managed by this controller
///@param data the rgb data to write out to the strip
///@param nLeds the number of leds being written out
///@param scale the rgb scaling to apply to each led before writing it out
virtualvoidshow(conststructCRGB *data, int nLeds, CRGB scale) {
PixelController<RGB_ORDER, LANES, MASK> pixels(data, nLeds, scale, getDither());
showPixels(pixels);
}
public:
CPixelLEDController() : CLEDController() {}
};
FASTLED_NAMESPACE_END
#endif