RSeries astromech firmware
NeoPixel_FastLED.h
Go to the documentation of this file.
1 // The MIT License (MIT)
2 //
3 // Copyright (c) 2013 FastLED
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy of
6 // this software and associated documentation files (the "Software"), to deal in
7 // the Software without restriction, including without limitation the rights to
8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 // the Software, and to permit persons to whom the Software is furnished to do so,
10 // subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in all
13 // copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22 #ifndef NEOPIXEL_FASTLED_H
23 #define NEOPIXEL_FASTLED_H
24 
25 #if defined(__arm__)
26 #define SCALE8_C 1
27 #elif defined(REELTWO_AVR)
28 // AVR ATmega and friends Arduino
29 #if !defined(LIB8_ATTINY)
30 #define SCALE8_AVRASM 1
31 #define CLEANUP_R1_AVRASM 1
32 #else
33 // On ATtiny, we just use C implementations
34 #define SCALE8_C 1
35 #define SCALE8_AVRASM 0
36 #endif
37 #else
38 #define SCALE8_C 1
39 #endif
40 
42 enum LEDChipset
43 {
44  WS2812B = NEO_GRB + NEO_KHZ800,
45  SK6812 = NEO_GRB + NEO_KHZ800,
46  SK6812CUSTOM = NEO_GRB + NEO_KHZ800
47 };
48 
50 extern void hsv2rgb_rainbow( const struct CHSV& hsv, struct CRGB& rgb);
51 
53 struct CHSV {
54  union {
55  struct {
56  union {
57  uint8_t hue;
58  uint8_t h; };
59  union {
60  uint8_t saturation;
61  uint8_t sat;
62  uint8_t s; };
63  union {
64  uint8_t value;
65  uint8_t val;
66  uint8_t v; };
67  };
68  uint8_t raw[3];
69  };
70 
72  inline CHSV() __attribute__((always_inline))
73  {
74  }
75 
77  inline CHSV( uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline))
78  : h(ih), s(is), v(iv)
79  {
80  }
81 
83  inline CHSV(const CHSV& rhs) __attribute__((always_inline))
84  {
85  h = rhs.h;
86  s = rhs.s;
87  v = rhs.v;
88  }
89 
90  inline CHSV& operator= (const CHSV& rhs) __attribute__((always_inline))
91  {
92  h = rhs.h;
93  s = rhs.s;
94  v = rhs.v;
95  return *this;
96  }
97 
98  inline CHSV& setHSV(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline))
99  {
100  h = ih;
101  s = is;
102  v = iv;
103  return *this;
104  }
105 };
106 
108 struct CRGB {
109  union {
110  struct {
111  union {
112  uint8_t g;
113  uint8_t green;
114  };
115  union {
116  uint8_t r;
117  uint8_t red;
118  };
119  union {
120  uint8_t b;
121  uint8_t blue;
122  };
123  };
124  uint8_t raw[3];
125  };
126 
127  // default values are UNINITIALIZED
128  inline CRGB() __attribute__((always_inline))
129  {
130  }
131 
133  inline CRGB( uint8_t ir, uint8_t ig, uint8_t ib) __attribute__((always_inline))
134  : g(ig), r(ir), b(ib)
135  {
136  }
137 
139  inline CRGB& setHSV (uint8_t hue, uint8_t sat, uint8_t val) __attribute__((always_inline))
140  {
141  hsv2rgb_rainbow( CHSV(hue, sat, val), *this);
142  return *this;
143  }
144 
146  inline CRGB& setRGB (uint8_t nr, uint8_t ng, uint8_t nb) __attribute__((always_inline))
147  {
148  r = nr;
149  g = ng;
150  b = nb;
151  return *this;
152  }
153 };
154 
156 typedef uint8_t fract8;
157 
159 #define LIB8STATIC __attribute__ ((unused)) static inline
160 #define LIB8STATIC_ALWAYS_INLINE __attribute__ ((always_inline)) static inline
162 
164 #define FASTLED_RAND16_2053 ((uint16_t)(2053))
165 #define FASTLED_RAND16_13849 ((uint16_t)(13849))
167 #define RAND16_SEED 1337
169 
171 static uint16_t rand16seed = RAND16_SEED;
172 
174 LIB8STATIC uint8_t random8()
175 {
176  rand16seed = (rand16seed * FASTLED_RAND16_2053) + FASTLED_RAND16_13849;
177  // return the sum of the high and low bytes, for better
178  // mixing and non-sequential correlation
179  return (uint8_t)(((uint8_t)(rand16seed & 0xFF)) +
180  ((uint8_t)(rand16seed >> 8)));
181 }
182 
184 LIB8STATIC uint8_t random8(uint8_t lim)
185 {
186  uint8_t r = random8();
187  r = (r*lim) >> 8;
188  return r;
189 }
190 
192 LIB8STATIC_ALWAYS_INLINE uint8_t scale8( uint8_t i, fract8 scale)
193 {
194 #if SCALE8_C == 1
195  return (((uint16_t)i) * (1+(uint16_t)(scale))) >> 8;
196 #elif SCALE8_AVRASM == 1
197 #if defined(LIB8_ATTINY)
198  uint8_t work=i;
199  uint8_t cnt=0x80;
200  asm volatile(
201  " inc %[scale] \n\t"
202  " breq DONE_%= \n\t"
203  " clr %[work] \n\t"
204  "LOOP_%=: \n\t"
205  /*" sbrc %[scale], 0 \n\t"
206  " add %[work], %[i] \n\t"
207  " ror %[work] \n\t"
208  " lsr %[scale] \n\t"
209  " clc \n\t"*/
210  " sbrc %[scale], 0 \n\t"
211  " add %[work], %[i] \n\t"
212  " ror %[work] \n\t"
213  " lsr %[scale] \n\t"
214  " lsr %[cnt] \n\t"
215  "brcc LOOP_%= \n\t"
216  "DONE_%=: \n\t"
217  : [work] "+r" (work), [cnt] "+r" (cnt)
218  : [scale] "r" (scale), [i] "r" (i)
219  :
220  );
221  return work;
222 #else
223  asm volatile(
224  // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
225  "mul %0, %1 \n\t"
226  // Add i to r0, possibly setting the carry flag
227  "add r0, %0 \n\t"
228  // load the immediate 0 into i (note, this does _not_ touch any flags)
229  "ldi %0, 0x00 \n\t"
230  // walk and chew gum at the same time
231  "adc %0, r1 \n\t"
232  "clr __zero_reg__ \n\t"
233 
234  : "+a" (i) /* writes to i */
235  : "a" (scale) /* uses scale */
236  : "r0", "r1" /* clobbers r0, r1 */ );
237 
238  /* Return the result */
239  return i;
240 #endif
241 #else
242 #error "No implementation for scale8 available."
243 #endif
244 }
245 
247 LIB8STATIC uint8_t map8( uint8_t in, uint8_t rangeStart, uint8_t rangeEnd)
248 {
249  uint8_t rangeWidth = rangeEnd - rangeStart;
250  uint8_t out = scale8( in, rangeWidth);
251  out += rangeStart;
252  return out;
253 }
254 
256 LIB8STATIC_ALWAYS_INLINE void cleanup_R1()
257 {
258 #if CLEANUP_R1_AVRASM == 1
259  // Restore r1 to "0"; it's expected to always be that
260  asm volatile( "clr __zero_reg__ \n\t" : : : "r1" );
261 #endif
262 }
263 
265 LIB8STATIC_ALWAYS_INLINE uint8_t scale8_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
266 {
267 #if SCALE8_C == 1
268  return (((uint16_t)i) * ((uint16_t)(scale)+1)) >> 8;
269 #elif SCALE8_AVRASM == 1
270  asm volatile(
271  // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
272  "mul %0, %1 \n\t"
273  // Add i to r0, possibly setting the carry flag
274  "add r0, %0 \n\t"
275  // load the immediate 0 into i (note, this does _not_ touch any flags)
276  "ldi %0, 0x00 \n\t"
277  // walk and chew gum at the same time
278  "adc %0, r1 \n\t"
279  /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */
280  /* "clr __zero_reg__ \n\t" */
281 
282  : "+a" (i) /* writes to i */
283  : "a" (scale) /* uses scale */
284  : "r0", "r1" /* clobbers r0, r1 */ );
285 
286  // Return the result
287  return i;
288 #else
289 #error "No implementation for scale8_LEAVING_R1_DIRTY available."
290 #endif
291 }
292 
294 LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
295 {
296 #if SCALE8_C == 1 || defined(LIB8_ATTINY)
297  uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
298  // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
299  // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
300  return j;
301 #elif SCALE8_AVRASM == 1
302  uint8_t j=0;
303  asm volatile(
304  " tst %[i]\n\t"
305  " breq L_%=\n\t"
306  " mul %[i], %[scale]\n\t"
307  " mov %[j], r1\n\t"
308  " breq L_%=\n\t"
309  " subi %[j], 0xFF\n\t"
310  "L_%=: \n\t"
311  : [j] "+a" (j)
312  : [i] "a" (i), [scale] "a" (scale)
313  : "r0", "r1");
314 
315  return j;
316 #else
317 #error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
318 #endif
319 }
320 
322 #define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) )
323 
324 
326 #define K255 255
327 #define K171 171
329 #define K170 170
331 #define K85 85
333 
335 void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb)
336 {
337  // Yellow has a higher inherent brightness than
338  // any other color; 'pure' yellow is perceived to
339  // be 93% as bright as white. In order to make
340  // yellow appear the correct relative brightness,
341  // it has to be rendered brighter than all other
342  // colors.
343  // Level Y1 is a moderate boost, the default.
344  // Level Y2 is a strong boost.
345  const uint8_t Y1 = 1;
346  const uint8_t Y2 = 0;
347 
348  // G2: Whether to divide all greens by two.
349  // Depends GREATLY on your particular LEDs
350  const uint8_t G2 = 0;
351 
352  // Gscale: what to scale green down by.
353  // Depends GREATLY on your particular LEDs
354  const uint8_t Gscale = 0;
355 
356 
357  uint8_t hue = hsv.hue;
358  uint8_t sat = hsv.sat;
359  uint8_t val = hsv.val;
360 
361  uint8_t offset = hue & 0x1F; // 0..31
362 
363  // offset8 = offset * 8
364  uint8_t offset8 = offset;
365  {
366 #if defined(__AVR__)
367  // Left to its own devices, gcc turns "x <<= 3" into a loop
368  // It's much faster and smaller to just do three single-bit shifts
369  // So this business is to force that.
370  offset8 <<= 1;
371  asm volatile("");
372  offset8 <<= 1;
373  asm volatile("");
374  offset8 <<= 1;
375 #else
376  // On ARM and other non-AVR platforms, we just shift 3.
377  offset8 <<= 3;
378 #endif
379  }
380 
381  uint8_t third = scale8( offset8, (256 / 3)); // max = 85
382 
383  uint8_t r, g, b;
384 
385  if( ! (hue & 0x80) ) {
386  // 0XX
387  if( ! (hue & 0x40) ) {
388  // 00X
389  //section 0-1
390  if( ! (hue & 0x20) ) {
391  // 000
392  //case 0: // R -> O
393  r = K255 - third;
394  g = third;
395  b = 0;
396  FORCE_REFERENCE(b);
397  } else {
398  // 001
399  //case 1: // O -> Y
400  if( Y1 ) {
401  r = K171;
402  g = K85 + third ;
403  b = 0;
404  FORCE_REFERENCE(b);
405  }
406  if( Y2 ) {
407  r = K170 + third;
408  //uint8_t twothirds = (third << 1);
409  uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170
410  g = K85 + twothirds;
411  b = 0;
412  FORCE_REFERENCE(b);
413  }
414  }
415  } else {
416  //01X
417  // section 2-3
418  if( ! (hue & 0x20) ) {
419  // 010
420  //case 2: // Y -> G
421  if( Y1 ) {
422  //uint8_t twothirds = (third << 1);
423  uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170
424  r = K171 - twothirds;
425  g = K170 + third;
426  b = 0;
427  FORCE_REFERENCE(b);
428  }
429  if( Y2 ) {
430  r = K255 - offset8;
431  g = K255;
432  b = 0;
433  FORCE_REFERENCE(b);
434  }
435  } else {
436  // 011
437  // case 3: // G -> A
438  r = 0;
439  FORCE_REFERENCE(r);
440  g = K255 - third;
441  b = third;
442  }
443  }
444  } else {
445  // section 4-7
446  // 1XX
447  if( ! (hue & 0x40) ) {
448  // 10X
449  if( ! ( hue & 0x20) ) {
450  // 100
451  //case 4: // A -> B
452  r = 0;
453  FORCE_REFERENCE(r);
454  //uint8_t twothirds = (third << 1);
455  uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170
456  g = K171 - twothirds; //K170?
457  b = K85 + twothirds;
458 
459  } else {
460  // 101
461  //case 5: // B -> P
462  r = third;
463  g = 0;
464  FORCE_REFERENCE(g);
465  b = K255 - third;
466 
467  }
468  } else {
469  if( ! (hue & 0x20) ) {
470  // 110
471  //case 6: // P -- K
472  r = K85 + third;
473  g = 0;
474  FORCE_REFERENCE(g);
475  b = K171 - third;
476 
477  } else {
478  // 111
479  //case 7: // K -> R
480  r = K170 + third;
481  g = 0;
482  FORCE_REFERENCE(g);
483  b = K85 - third;
484 
485  }
486  }
487  }
488 
489  // This is one of the good places to scale the green down,
490  // although the client can scale green down as well.
491  if( G2 ) g = g >> 1;
492  if( Gscale ) g = scale8_video_LEAVING_R1_DIRTY( g, Gscale);
493 
494  // Scale down colors if we're desaturated at all
495  // and add the brightness_floor to r, g, and b.
496  if( sat != 255 ) {
497  if( sat == 0) {
498  r = 255; b = 255; g = 255;
499  } else {
500  //nscale8x3_video( r, g, b, sat);
501  if( r ) r = scale8_LEAVING_R1_DIRTY( r, sat);
502  if( g ) g = scale8_LEAVING_R1_DIRTY( g, sat);
503  if( b ) b = scale8_LEAVING_R1_DIRTY( b, sat);
504  cleanup_R1();
505 
506  uint8_t desat = 255 - sat;
507  desat = scale8( desat, desat);
508 
509  uint8_t brightness_floor = desat;
510  r += brightness_floor;
511  g += brightness_floor;
512  b += brightness_floor;
513  }
514  }
515 
516  // Now scale everything down if we're at value < 255.
517  if( val != 255 ) {
518 
519  val = scale8_video_LEAVING_R1_DIRTY( val, val);
520  if( val == 0 ) {
521  r=0; g=0; b=0;
522  } else {
523  // nscale8x3_video( r, g, b, val);
524  if( r ) r = scale8_LEAVING_R1_DIRTY( r, val);
525  if( g ) g = scale8_LEAVING_R1_DIRTY( g, val);
526  if( b ) b = scale8_LEAVING_R1_DIRTY( b, val);
527  cleanup_R1();
528  }
529  }
530 
531  // Here we have the old AVR "missing std X+n" problem again
532  // It turns out that fixing it winds up costing more than
533  // not fixing it.
534  // To paraphrase Dr Bronner, profile! profile! profile!
535  //asm volatile( "" : : : "r26", "r27" );
536  //asm volatile (" movw r30, r26 \n" : : : "r30", "r31");
537  rgb.r = r;
538  rgb.g = g;
539  rgb.b = b;
540 }
541 
542 #endif
K171
#define K171
Definition: NeoPixel_FastLED.h:328
FASTLED_RAND16_13849
#define FASTLED_RAND16_13849
Definition: NeoPixel_FastLED.h:166
FORCE_REFERENCE
#define FORCE_REFERENCE(var)
Definition: NeoPixel_FastLED.h:322
K255
#define K255
Definition: NeoPixel_FastLED.h:326
LIB8STATIC
#define LIB8STATIC
Definition: NeoPixel_FastLED.h:159
LIB8STATIC_ALWAYS_INLINE
#define LIB8STATIC_ALWAYS_INLINE
Definition: NeoPixel_FastLED.h:161
K85
#define K85
Definition: NeoPixel_FastLED.h:332
RAND16_SEED
#define RAND16_SEED
Definition: NeoPixel_FastLED.h:168
K170
#define K170
Definition: NeoPixel_FastLED.h:330
FASTLED_RAND16_2053
#define FASTLED_RAND16_2053
Definition: NeoPixel_FastLED.h:164