forked from FastLED/FastLED
-
Notifications
You must be signed in to change notification settings - Fork 0
/
controller.h
407 lines (338 loc) · 18.7 KB
/
controller.h
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
#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
#define RO(X) RGB_BYTE(RGB_ORDER, X)
#define RGB_BYTE(RO,X) (((RO)>>(3*(2-(X)))) & 0x3)
#define RGB_BYTE0(RO) ((RO>>6) & 0x3)
#define RGB_BYTE1(RO) ((RO>>3) & 0x3)
#define RGB_BYTE2(RO) ((RO) & 0x3)
// operator byte *(struct CRGB[] arr) { return (byte*)arr; }
#define DISABLE_DITHER 0x00
#define BINARY_DITHER 0x01
typedef uint8_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
class CLEDController {
protected:
friend class CFastLED;
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
virtual void showColor(const struct CRGB & 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
virtual void show(const struct CRGB *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
virtual void init() = 0;
///clear out/zero out the given number of leds.
virtual void clearLeds(int nLeds) { showColor(CRGB::Black, nLeds, CRGB::Black); }
/// show function w/integer brightness, will scale for color correction and temperature
void show(const struct CRGB *data, int nLeds, uint8_t brightness) {
show(data, nLeds, getAdjustment(brightness));
}
/// show function w/integer brightness, will scale for color correction and temperature
void showColor(const struct CRGB &data, int nLeds, uint8_t brightness) {
showColor(data, nLeds, getAdjustment(brightness));
}
/// show function using the "attached to this controller" led data
void showLeds(uint8_t brightness=255) {
show(m_Data, m_nLeds, getAdjustment(brightness));
}
/// show the given color on the led strip
void showColor(const struct CRGB & 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
void clearLedData() {
if(m_Data) {
memset8((void*)m_Data, 0, sizeof(struct CRGB) * m_nLeds);
}
}
/// How many leds does this controller manage?
virtual int size() { 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
inline uint8_t getDither() { 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
CRGB getCorrection() { 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
CRGB getTemperature() { return m_ColorTemperature; }
/// Get the combined brightness/color adjustment for this controller
CRGB getAdjustment(uint8_t scale) {
return computeAdjustment(scale, m_ColorCorrection, m_ColorTemperature);
}
static CRGB computeAdjustment(uint8_t scale, const CRGB & colorCorrection, const CRGB & colorTemperature) {
#if defined(NO_CORRECTION) && (NO_CORRECTION==1)
return CRGB(scale,scale,scale);
#else
CRGB adj(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
}
virtual uint16_t getMaxRefreshRate() const { return 0; }
};
// 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, int LANES=1, uint32_t MASK=0xFFFFFFFF>
struct PixelController {
const uint8_t *mData;
int mLen,mLenRemaining;
uint8_t d[3];
uint8_t e[3];
CRGB mScale;
int8_t mAdvance;
int mOffsets[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]; }
}
void initOffsets(int len) {
int nOffset = 0;
for(int i = 0; i < LANES; i++) {
mOffsets[i] = nOffset;
if((1<<i) & MASK) { nOffset += (len * mAdvance); }
}
}
PixelController(const uint8_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(const CRGB *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)d), mLen(len), mLenRemaining(len), mScale(s) {
enable_dithering(dither);
mAdvance = 3;
initOffsets(len);
}
PixelController(const CRGB &d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)&d), mLen(len), mLenRemaining(len), mScale(s) {
enable_dithering(dither);
mAdvance = 0;
initOffsets(len);
}
void init_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.
#define MAX_LIKELY_UPDATE_RATE_HZ 400
#define MIN_ACCEPTABLE_DITHER_RATE_HZ 50
#define UPDATES_PER_FULL_DITHER_CYCLE (MAX_LIKELY_UPDATE_RATE_HZ / MIN_ACCEPTABLE_DITHER_RATE_HZ)
#define RECOMMENDED_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) )
#define VIRTUAL_BITS RECOMMENDED_VIRTUAL_BITS
// R is the digther signal 'counter'.
static uint8_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)) inline bool has(int n) {
return mLenRemaining >= n;
}
// toggle dithering enable
void enable_dithering(EDitherMode dither) {
switch(dither) {
case BINARY_DITHER: init_binary_dithering(); break;
default: d[0]=d[1]=d[2]=e[0]=e[1]=e[2]=0; break;
}
}
__attribute__((always_inline)) inline int size() { return mLen; }
// get the amount to advance the pointer by
__attribute__((always_inline)) inline int advanceBy() { return mAdvance; }
// advance the data pointer forward, adjust position counter
__attribute__((always_inline)) inline void advanceData() { mData += mAdvance; mLenRemaining--;}
// step the dithering forward
__attribute__((always_inline)) inline void stepDithering() {
// 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)) inline void preStepFirstByteDithering() {
d[RO(0)] = e[RO(0)] - d[RO(0)];
}
template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc) { return pc.mData[RO(SLOT)]; }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc, int lane) { return pc.mData[pc.mOffsets[lane] + RO(SLOT)]; }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t dither(PixelController & pc, uint8_t b) { return b ? qadd8(b, pc.d[RO(SLOT)]) : 0; }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t dither(PixelController & , uint8_t b, uint8_t d) { return b ? qadd8(b,d) : 0; }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t scale(PixelController & pc, uint8_t b) { return scale8(b, pc.mScale.raw[RO(SLOT)]); }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t scale(PixelController & , uint8_t b, uint8_t scale) { return scale8(b, scale); }
// composite shortcut functions for loading, dithering, and scaling
template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc) { return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc))); }
template<int SLOT> __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<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t d, uint8_t scale) { return scale8(pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc, lane), d), scale); }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t scale) { return scale8(pc.loadByte<SLOT>(pc, lane), scale); }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc); }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc, lane); }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t getd(PixelController & pc) { return pc.d[RO(SLOT)]; }
template<int SLOT> __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)) inline uint8_t loadAndScale0(int lane) { return loadAndScale<0>(*this, lane); }
__attribute__((always_inline)) inline uint8_t loadAndScale1(int lane) { return loadAndScale<1>(*this, lane); }
__attribute__((always_inline)) inline uint8_t loadAndScale2(int lane) { return loadAndScale<2>(*this, lane); }
__attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane) { return advanceAndLoadAndScale<0>(*this, lane); }
__attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane); }
__attribute__((always_inline)) inline uint8_t loadAndScale0() { return loadAndScale<0>(*this); }
__attribute__((always_inline)) inline uint8_t loadAndScale1() { return loadAndScale<1>(*this); }
__attribute__((always_inline)) inline uint8_t loadAndScale2() { return loadAndScale<2>(*this); }
__attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0() { return advanceAndLoadAndScale<0>(*this); }
__attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0() { stepDithering(); return advanceAndLoadAndScale<0>(*this); }
};
template<EOrder RGB_ORDER, int LANES=1, uint32_t MASK=0xFFFFFFFF> class CPixelLEDController : public CLEDController {
protected:
virtual void showPixels(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
virtual void showColor(const struct CRGB & 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
virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
PixelController<RGB_ORDER, LANES, MASK> pixels(data, nLeds, scale, getDither());
showPixels(pixels);
}
public:
CPixelLEDController() : CLEDController() {}
};
FASTLED_NAMESPACE_END
#endif