FastLED  3.1
controller.h
Go to the documentation of this file.
1 #ifndef __INC_CONTROLLER_H
2 #define __INC_CONTROLLER_H
3 
6 
7 #include "FastLED.h"
8 #include "led_sysdefs.h"
9 #include "pixeltypes.h"
10 #include "color.h"
11 
12 FASTLED_NAMESPACE_BEGIN
13 
14 #define RO(X) RGB_BYTE(RGB_ORDER, X)
15 #define RGB_BYTE(RO,X) (((RO)>>(3*(2-(X)))) & 0x3)
16 
17 #define RGB_BYTE0(RO) ((RO>>6) & 0x3)
18 #define RGB_BYTE1(RO) ((RO>>3) & 0x3)
19 #define RGB_BYTE2(RO) ((RO) & 0x3)
20 
21 // operator byte *(struct CRGB[] arr) { return (byte*)arr; }
22 
23 #define DISABLE_DITHER 0x00
24 #define BINARY_DITHER 0x01
25 typedef uint8_t EDitherMode;
26 
28 //
29 // LED Controller interface definition
30 //
32 
39 protected:
40  friend class CFastLED;
41  CRGB *m_Data;
42  CLEDController *m_pNext;
43  CRGB m_ColorCorrection;
44  CRGB m_ColorTemperature;
45  EDitherMode m_DitherMode;
46  int m_nLeds;
47  static CLEDController *m_pHead;
48  static CLEDController *m_pTail;
49 
54  virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) = 0;
55 
60  virtual void show(const struct CRGB *data, int nLeds, CRGB scale) = 0;
61 
62 public:
64  CLEDController() : m_Data(NULL), m_ColorCorrection(UncorrectedColor), m_ColorTemperature(UncorrectedTemperature), m_DitherMode(BINARY_DITHER), m_nLeds(0) {
65  m_pNext = NULL;
66  if(m_pHead==NULL) { m_pHead = this; }
67  if(m_pTail != NULL) { m_pTail->m_pNext = this; }
68  m_pTail = this;
69  }
70 
72  virtual void init() = 0;
73 
75  virtual void clearLeds(int nLeds) { showColor(CRGB::Black, nLeds, CRGB::Black); }
76 
78  void show(const struct CRGB *data, int nLeds, uint8_t brightness) {
79  show(data, nLeds, getAdjustment(brightness));
80  }
81 
83  void showColor(const struct CRGB &data, int nLeds, uint8_t brightness) {
84  showColor(data, nLeds, getAdjustment(brightness));
85  }
86 
88  void showLeds(uint8_t brightness=255) {
89  show(m_Data, m_nLeds, getAdjustment(brightness));
90  }
91 
93  void showColor(const struct CRGB & data, uint8_t brightness=255) {
94  showColor(data, m_nLeds, getAdjustment(brightness));
95  }
96 
98  static CLEDController *head() { return m_pHead; }
100  CLEDController *next() { return m_pNext; }
101 
103  CLEDController & setLeds(CRGB *data, int nLeds) {
104  m_Data = data;
105  m_nLeds = nLeds;
106  return *this;
107  }
108 
110  void clearLedData() {
111  if(m_Data) {
112  memset8((void*)m_Data, 0, sizeof(struct CRGB) * m_nLeds);
113  }
114  }
115 
117  virtual int size() { return m_nLeds; }
118 
120  CRGB* leds() { return m_Data; }
121 
123  CRGB &operator[](int x) { return m_Data[x]; }
124 
126  inline CLEDController & setDither(uint8_t ditherMode = BINARY_DITHER) { m_DitherMode = ditherMode; return *this; }
128  inline uint8_t getDither() { return m_DitherMode; }
129 
131  CLEDController & setCorrection(CRGB correction) { m_ColorCorrection = correction; return *this; }
133  CLEDController & setCorrection(LEDColorCorrection correction) { m_ColorCorrection = correction; return *this; }
135  CRGB getCorrection() { return m_ColorCorrection; }
136 
138  CLEDController & setTemperature(CRGB temperature) { m_ColorTemperature = temperature; return *this; }
140  CLEDController & setTemperature(ColorTemperature temperature) { m_ColorTemperature = temperature; return *this; }
142  CRGB getTemperature() { return m_ColorTemperature; }
143 
145  CRGB getAdjustment(uint8_t scale) {
146  return computeAdjustment(scale, m_ColorCorrection, m_ColorTemperature);
147  }
148 
149  static CRGB computeAdjustment(uint8_t scale, const CRGB & colorCorrection, const CRGB & colorTemperature) {
150  #if defined(NO_CORRECTION) && (NO_CORRECTION==1)
151  return CRGB(scale,scale,scale);
152  #else
153  CRGB adj(0,0,0);
154 
155  if(scale > 0) {
156  for(uint8_t i = 0; i < 3; i++) {
157  uint8_t cc = colorCorrection.raw[i];
158  uint8_t ct = colorTemperature.raw[i];
159  if(cc > 0 && ct > 0) {
160  uint32_t work = (((uint32_t)cc)+1) * (((uint32_t)ct)+1) * scale;
161  work /= 0x10000L;
162  adj.raw[i] = work & 0xFF;
163  }
164  }
165  }
166 
167  return adj;
168  #endif
169  }
170  virtual uint16_t getMaxRefreshRate() const { return 0; }
171 };
172 
173 // Pixel controller class. This is the class that we use to centralize pixel access in a block of data, including
174 // support for things like RGB reordering, scaling, dithering, skipping (for ARGB data), and eventually, we will
175 // centralize 8/12/16 conversions here as well.
176 template<EOrder RGB_ORDER, int LANES=1, uint32_t MASK=0xFFFFFFFF>
178  const uint8_t *mData;
179  int mLen,mLenRemaining;
180  uint8_t d[3];
181  uint8_t e[3];
182  CRGB mScale;
183  int8_t mAdvance;
184  int mOffsets[LANES];
185 
186  PixelController(const PixelController & other) {
187  d[0] = other.d[0];
188  d[1] = other.d[1];
189  d[2] = other.d[2];
190  e[0] = other.e[0];
191  e[1] = other.e[1];
192  e[2] = other.e[2];
193  mData = other.mData;
194  mScale = other.mScale;
195  mAdvance = other.mAdvance;
196  mLenRemaining = mLen = other.mLen;
197  for(int i = 0; i < LANES; i++) { mOffsets[i] = other.mOffsets[i]; }
198 
199  }
200 
201  void initOffsets(int len) {
202  int nOffset = 0;
203  for(int i = 0; i < LANES; i++) {
204  mOffsets[i] = nOffset;
205  if((1<<i) & MASK) { nOffset += (len * mAdvance); }
206  }
207  }
208 
209  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) {
210  enable_dithering(dither);
211  mData += skip;
212  mAdvance = (advance) ? 3+skip : 0;
213  initOffsets(len);
214  }
215 
216  PixelController(const CRGB *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)d), mLen(len), mLenRemaining(len), mScale(s) {
217  enable_dithering(dither);
218  mAdvance = 3;
219  initOffsets(len);
220  }
221 
222  PixelController(const CRGB &d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)&d), mLen(len), mLenRemaining(len), mScale(s) {
223  enable_dithering(dither);
224  mAdvance = 0;
225  initOffsets(len);
226  }
227 
228  void init_binary_dithering() {
229 #if !defined(NO_DITHERING) || (NO_DITHERING != 1)
230 
231  // Set 'virtual bits' of dithering to the highest level
232  // that is not likely to cause excessive flickering at
233  // low brightness levels + low update rates.
234  // These pre-set values are a little ambitious, since
235  // a 400Hz update rate for WS2811-family LEDs is only
236  // possible with 85 pixels or fewer.
237  // Once we have a 'number of milliseconds since last update'
238  // value available here, we can quickly calculate the correct
239  // number of 'virtual bits' on the fly with a couple of 'if'
240  // statements -- no division required. At this point,
241  // the division is done at compile time, so there's no runtime
242  // cost, but the values are still hard-coded.
243 #define MAX_LIKELY_UPDATE_RATE_HZ 400
244 #define MIN_ACCEPTABLE_DITHER_RATE_HZ 50
245 #define UPDATES_PER_FULL_DITHER_CYCLE (MAX_LIKELY_UPDATE_RATE_HZ / MIN_ACCEPTABLE_DITHER_RATE_HZ)
246 #define RECOMMENDED_VIRTUAL_BITS ((UPDATES_PER_FULL_DITHER_CYCLE>1) + \
247  (UPDATES_PER_FULL_DITHER_CYCLE>2) + \
248  (UPDATES_PER_FULL_DITHER_CYCLE>4) + \
249  (UPDATES_PER_FULL_DITHER_CYCLE>8) + \
250  (UPDATES_PER_FULL_DITHER_CYCLE>16) + \
251  (UPDATES_PER_FULL_DITHER_CYCLE>32) + \
252  (UPDATES_PER_FULL_DITHER_CYCLE>64) + \
253  (UPDATES_PER_FULL_DITHER_CYCLE>128) )
254 #define VIRTUAL_BITS RECOMMENDED_VIRTUAL_BITS
255 
256  // R is the digther signal 'counter'.
257  static byte R = 0;
258  R++;
259 
260  // R is wrapped around at 2^ditherBits,
261  // so if ditherBits is 2, R will cycle through (0,1,2,3)
262  byte ditherBits = VIRTUAL_BITS;
263  R &= (0x01 << ditherBits) - 1;
264 
265  // Q is the "unscaled dither signal" itself.
266  // It's initialized to the reversed bits of R.
267  // If 'ditherBits' is 2, Q here will cycle through (0,128,64,192)
268  byte Q = 0;
269 
270  // Reverse bits in a byte
271  {
272  if(R & 0x01) { Q |= 0x80; }
273  if(R & 0x02) { Q |= 0x40; }
274  if(R & 0x04) { Q |= 0x20; }
275  if(R & 0x08) { Q |= 0x10; }
276  if(R & 0x10) { Q |= 0x08; }
277  if(R & 0x20) { Q |= 0x04; }
278  if(R & 0x40) { Q |= 0x02; }
279  if(R & 0x80) { Q |= 0x01; }
280  }
281 
282  // Now we adjust Q to fall in the center of each range,
283  // instead of at the start of the range.
284  // If ditherBits is 2, Q will be (0, 128, 64, 192) at first,
285  // and this adjustment makes it (31, 159, 95, 223).
286  if( ditherBits < 8) {
287  Q += 0x01 << (7 - ditherBits);
288  }
289 
290  // D and E form the "scaled dither signal"
291  // which is added to pixel values to affect the
292  // actual dithering.
293 
294  // Setup the initial D and E values
295  for(int i = 0; i < 3; i++) {
296  byte s = mScale.raw[i];
297  e[i] = s ? (256/s) + 1 : 0;
298  d[i] = scale8(Q, e[i]);
299 #if (FASTLED_SCALE8_FIXED == 1)
300  if(d[i]) (d[i]--);
301 #endif
302  if(e[i]) e[i]--;
303  }
304 #endif
305  }
306 
307  // Do we have n pixels left to process?
308  __attribute__((always_inline)) inline bool has(int n) {
309  return mLenRemaining >= n;
310  }
311 
312  // toggle dithering enable
313  void enable_dithering(EDitherMode dither) {
314  switch(dither) {
315  case BINARY_DITHER: init_binary_dithering(); break;
316  default: d[0]=d[1]=d[2]=e[0]=e[1]=e[2]=0; break;
317  }
318  }
319 
320  __attribute__((always_inline)) inline int size() { return mLen; }
321 
322  // get the amount to advance the pointer by
323  __attribute__((always_inline)) inline int advanceBy() { return mAdvance; }
324 
325  // advance the data pointer forward, adjust position counter
326  __attribute__((always_inline)) inline void advanceData() { mData += mAdvance; mLenRemaining--;}
327 
328  // step the dithering forward
329  __attribute__((always_inline)) inline void stepDithering() {
330  // IF UPDATING HERE, BE SURE TO UPDATE THE ASM VERSION IN
331  // clockless_trinket.h!
332  d[0] = e[0] - d[0];
333  d[1] = e[1] - d[1];
334  d[2] = e[2] - d[2];
335  }
336 
337  // Some chipsets pre-cycle the first byte, which means we want to cycle byte 0's dithering separately
338  __attribute__((always_inline)) inline void preStepFirstByteDithering() {
339  d[RO(0)] = e[RO(0)] - d[RO(0)];
340  }
341 
342  template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc) { return pc.mData[RO(SLOT)]; }
343  template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc, int lane) { return pc.mData[pc.mOffsets[lane] + RO(SLOT)]; }
344 
345  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; }
346  template<int SLOT> __attribute__((always_inline)) inline static uint8_t dither(PixelController & , uint8_t b, uint8_t d) { return b ? qadd8(b,d) : 0; }
347 
348  template<int SLOT> __attribute__((always_inline)) inline static uint8_t scale(PixelController & pc, uint8_t b) { return scale8(b, pc.mScale.raw[RO(SLOT)]); }
349  template<int SLOT> __attribute__((always_inline)) inline static uint8_t scale(PixelController & , uint8_t b, uint8_t scale) { return scale8(b, scale); }
350 
351  // composite shortcut functions for loading, dithering, and scaling
352  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))); }
353  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))); }
354  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); }
355  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); }
356 
357  template<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc); }
358  template<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc, lane); }
359 
360  template<int SLOT> __attribute__((always_inline)) inline static uint8_t getd(PixelController & pc) { return pc.d[RO(SLOT)]; }
361  template<int SLOT> __attribute__((always_inline)) inline static uint8_t getscale(PixelController & pc) { return pc.mScale.raw[RO(SLOT)]; }
362 
363  // Helper functions to get around gcc stupidities
364  __attribute__((always_inline)) inline uint8_t loadAndScale0(int lane) { return loadAndScale<0>(*this, lane); }
365  __attribute__((always_inline)) inline uint8_t loadAndScale1(int lane) { return loadAndScale<1>(*this, lane); }
366  __attribute__((always_inline)) inline uint8_t loadAndScale2(int lane) { return loadAndScale<2>(*this, lane); }
367  __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane) { return advanceAndLoadAndScale<0>(*this, lane); }
368  __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane); }
369 
370  __attribute__((always_inline)) inline uint8_t loadAndScale0() { return loadAndScale<0>(*this); }
371  __attribute__((always_inline)) inline uint8_t loadAndScale1() { return loadAndScale<1>(*this); }
372  __attribute__((always_inline)) inline uint8_t loadAndScale2() { return loadAndScale<2>(*this); }
373  __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0() { return advanceAndLoadAndScale<0>(*this); }
374  __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0() { stepDithering(); return advanceAndLoadAndScale<0>(*this); }
375 };
376 
377 template<EOrder RGB_ORDER, int LANES=1, uint32_t MASK=0xFFFFFFFF> class CPixelLEDController : public CLEDController {
378 protected:
379  virtual void showPixels(PixelController<RGB_ORDER,LANES,MASK> & pixels) = 0;
380 
385  virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) {
386  PixelController<RGB_ORDER, LANES, MASK> pixels(data, nLeds, scale, getDither());
387  showPixels(pixels);
388  }
389 
394  virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
395  PixelController<RGB_ORDER, LANES, MASK> pixels(data, nLeds, scale, getDither());
396  showPixels(pixels);
397  }
398 
399 public:
401 };
402 
403 
404 FASTLED_NAMESPACE_END
405 
406 #endif
CRGB getAdjustment(uint8_t scale)
Get the combined brightness/color adjustment for this controller.
Definition: controller.h:145
void showLeds(uint8_t brightness=255)
show function using the "attached to this controller" led data
Definition: controller.h:88
virtual void show(const struct CRGB *data, int nLeds, CRGB scale)
write the passed in rgb data out to the leds managed by this controller
Definition: controller.h:394
virtual void clearLeds(int nLeds)
clear out/zero out the given number of leds.
Definition: controller.h:75
Representation of an RGB pixel (Red, Green, Blue)
Definition: pixeltypes.h:90
CRGB getCorrection()
get the correction value used by this controller
Definition: controller.h:135
uncorrected color
Definition: color.h:30
Base definition for an LED controller.
Definition: controller.h:38
void clearLedData()
zero out the led data managed by this controller
Definition: controller.h:110
virtual void showColor(const struct CRGB &data, int nLeds, CRGB scale)
set all the leds on the controller to a given color
Definition: controller.h:385
CLEDController & setTemperature(CRGB temperature)
set the color temperature, aka white point, for this controller
Definition: controller.h:138
CLEDController & setLeds(CRGB *data, int nLeds)
set the default array of leds to be used by this controller
Definition: controller.h:103
High level controller interface for FastLED.
Definition: FastLED.h:157
void showColor(const struct CRGB &data, int nLeds, uint8_t brightness)
show function w/integer brightness, will scale for color correction and temperature ...
Definition: controller.h:83
CLEDController & setCorrection(CRGB correction)
the the color corrction to use for this controller, expressed as an rgb object
Definition: controller.h:131
virtual void init()=0
initialize the LED controller
CRGB getTemperature()
get the color temperature, aka whipe point, for this controller
Definition: controller.h:142
void showColor(const struct CRGB &data, uint8_t brightness=255)
show the given color on the led strip
Definition: controller.h:93
CLEDController & setTemperature(ColorTemperature temperature)
set the color temperature, aka white point, for this controller
Definition: controller.h:140
CLEDController * next()
get the next controller in the chain after this one. will return NULL at the end of the chain ...
Definition: controller.h:100
Uncorrected temperature 0xFFFFFF.
Definition: color.h:78
CLEDController & setDither(uint8_t ditherMode=BINARY_DITHER)
set the dithering mode for this controller to use
Definition: controller.h:126
CRGB * leds()
Pointer to the CRGB array for this controller.
Definition: controller.h:120
LEDColorCorrection
Definition: color.h:13
central include file for FastLED, defines the CFastLED class/object
virtual void show(const struct CRGB *data, int nLeds, CRGB scale)=0
write the passed in rgb data out to the leds managed by this controller
contains definitions for color correction and temperature
LIB8STATIC_ALWAYS_INLINE uint8_t qadd8(uint8_t i, uint8_t j)
add one byte to another, saturating at 0xFF
Definition: math8.h:21
CLEDController()
create an led controller object, add it to the chain of controllers
Definition: controller.h:64
virtual void showColor(const struct CRGB &data, int nLeds, CRGB scale)=0
set all the leds on the controller to a given color
virtual int size()
How many leds does this controller manage?
Definition: controller.h:117
uint8_t getDither()
get the dithering option currently set for this controller
Definition: controller.h:128
CLEDController & setCorrection(LEDColorCorrection correction)
set the color correction to use for this controller
Definition: controller.h:133
void show(const struct CRGB *data, int nLeds, uint8_t brightness)
show function w/integer brightness, will scale for color correction and temperature ...
Definition: controller.h:78
CRGB & operator[](int x)
Reference to the n'th item in the controller.
Definition: controller.h:123
ColorTemperature
Definition: color.h:35
static CLEDController * head()
get the first led controller in the chain of controllers
Definition: controller.h:98
LIB8STATIC_ALWAYS_INLINE uint8_t scale8(uint8_t i, fract8 scale)
scale one byte by a second one, which is treated as the numerator of a fraction whose denominator is ...
Definition: scale8.h:20