1 #ifndef __INC_LIB8TION_SCALE_H
2 #define __INC_LIB8TION_SCALE_H
6 ///@defgroup Scaling Scaling functions
7 /// Fast, efficient 8-bit scaling functions specifically
8 /// designed for high-performance LED programming.
10 /// Because of the AVR(Arduino) and ARM assembly language
11 /// implementations provided, using these functions often
12 /// results in smaller and faster code than the equivalent
13 /// program using plain "C" arithmetic and logic.
16 /// scale one byte by a second one, which is treated as
17 /// the numerator of a fraction whose denominator is 256
18 /// In other words, it computes i * (scale / 256)
19 /// 4 clocks AVR with MUL, 2 clocks ARM
20 LIB8STATIC_ALWAYS_INLINE uint8_t scale8( uint8_t i, fract8 scale)
23 #if (FASTLED_SCALE8_FIXED == 1)
24 return (((uint16_t)i) * (1+(uint16_t)(scale))) >> 8;
26 return ((uint16_t)i * (uint16_t)(scale) ) >> 8;
28 #elif SCALE8_AVRASM == 1
29 #if defined(LIB8_ATTINY)
30 #if (FASTLED_SCALE8_FIXED == 1)
37 #if (FASTLED_SCALE8_FIXED == 1)
43 /*" sbrc %[scale], 0 \n\t"
44 " add %[work], %[i] \n\t"
48 " sbrc %[scale], 0 \n\t"
49 " add %[work], %[i] \n\t"
55 : [work] "+r" (work), [cnt] "+r" (cnt)
56 : [scale] "r" (scale), [i] "r" (i)
62 #if (FASTLED_SCALE8_FIXED==1)
63 // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
65 // Add i to r0, possibly setting the carry flag
67 // load the immediate 0 into i (note, this does _not_ touch any flags)
69 // walk and chew gum at the same time
72 /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
74 /* Move the high 8-bits of the product (r1) back to i */
76 /* Restore r1 to "0"; it's expected to always be that */
78 "clr __zero_reg__ \n\t"
80 : "+a" (i) /* writes to i */
81 : "a" (scale) /* uses scale */
82 : "r0", "r1" /* clobbers r0, r1 */ );
84 /* Return the result */
88 #error "No implementation for scale8 available."
93 /// The "video" version of scale8 guarantees that the output will
94 /// be only be zero if one or both of the inputs are zero. If both
95 /// inputs are non-zero, the output is guaranteed to be non-zero.
96 /// This makes for better 'video'/LED dimming, at the cost of
97 /// several additional cycles.
98 LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video( uint8_t i, fract8 scale)
100 #if SCALE8_C == 1 || defined(LIB8_ATTINY)
101 uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
102 // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
103 // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
105 #elif SCALE8_AVRASM == 1
110 " mul %[i], %[scale]\n\t"
112 " clr __zero_reg__\n\t"
113 " cpse %[scale], r1\n\t"
114 " subi %[j], 0xFF\n\t"
117 : [i] "a" (i), [scale] "a" (scale)
121 // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
128 // " clr __zero_reg__ \n"
132 // : "a" (scale), "a" (nonzeroscale)
135 // // Return the result
138 #error "No implementation for scale8_video available."
143 /// This version of scale8 does not clean up the R1 register on AVR
144 /// If you are doing several 'scale8's in a row, use this, and
145 /// then explicitly call cleanup_R1.
146 LIB8STATIC_ALWAYS_INLINE uint8_t scale8_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
149 #if (FASTLED_SCALE8_FIXED == 1)
150 return (((uint16_t)i) * ((uint16_t)(scale)+1)) >> 8;
152 return ((int)i * (int)(scale) ) >> 8;
154 #elif SCALE8_AVRASM == 1
156 #if (FASTLED_SCALE8_FIXED==1)
157 // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
159 // Add i to r0, possibly setting the carry flag
161 // load the immediate 0 into i (note, this does _not_ touch any flags)
163 // walk and chew gum at the same time
166 /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
168 /* Move the high 8-bits of the product (r1) back to i */
171 /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */
172 /* "clr __zero_reg__ \n\t" */
174 : "+a" (i) /* writes to i */
175 : "a" (scale) /* uses scale */
176 : "r0", "r1" /* clobbers r0, r1 */ );
181 #error "No implementation for scale8_LEAVING_R1_DIRTY available."
186 /// This version of scale8_video does not clean up the R1 register on AVR
187 /// If you are doing several 'scale8_video's in a row, use this, and
188 /// then explicitly call cleanup_R1.
189 LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
191 #if SCALE8_C == 1 || defined(LIB8_ATTINY)
192 uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
193 // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
194 // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
196 #elif SCALE8_AVRASM == 1
201 " mul %[i], %[scale]\n\t"
204 " subi %[j], 0xFF\n\t"
207 : [i] "a" (i), [scale] "a" (scale)
211 // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
218 // " clr __zero_reg__ \n"
222 // : "a" (scale), "a" (nonzeroscale)
225 // // Return the result
228 #error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
232 /// Clean up the r1 register after a series of *LEAVING_R1_DIRTY calls
233 LIB8STATIC_ALWAYS_INLINE void cleanup_R1(void)
235 #if CLEANUP_R1_AVRASM == 1
236 // Restore r1 to "0"; it's expected to always be that
237 asm volatile( "clr __zero_reg__ \n\t" : : : "r1" );
242 /// scale a 16-bit unsigned value by an 8-bit value,
243 /// considered as numerator of a fraction whose denominator
244 /// is 256. In other words, it computes i * (scale / 256)
246 LIB8STATIC_ALWAYS_INLINE uint16_t scale16by8( uint16_t i, fract8 scale )
248 #if SCALE16BY8_C == 1
250 #if FASTLED_SCALE8_FIXED == 1
251 result = (i * (1+((uint16_t)scale))) >> 8;
253 result = (i * scale) / 256;
256 #elif SCALE16BY8_AVRASM == 1
257 #if FASTLED_SCALE8_FIXED == 1
260 // result.A = HighByte( (i.A x scale) + i.A )
261 " mul %A[i], %[scale] \n\t"
262 " add r0, %A[i] \n\t"
263 // " adc r1, [zero] \n\t"
264 // " mov %A[result], r1 \n\t"
265 " adc %A[result], r1 \n\t"
267 // result.A-B += i.B x scale
268 " mul %B[i], %[scale] \n\t"
269 " add %A[result], r0 \n\t"
270 " adc %B[result], r1 \n\t"
273 " clr __zero_reg__ \n\t"
276 " add %A[result], %B[i] \n\t"
277 " adc %B[result], __zero_reg__ \n\t"
279 : [result] "+r" (result)
280 : [i] "r" (i), [scale] "r" (scale)
287 // result.A = HighByte(i.A x j )
288 " mul %A[i], %[scale] \n\t"
289 " mov %A[result], r1 \n\t"
290 //" clr %B[result] \n\t"
292 // result.A-B += i.B x j
293 " mul %B[i], %[scale] \n\t"
294 " add %A[result], r0 \n\t"
295 " adc %B[result], r1 \n\t"
298 " clr __zero_reg__ \n\t"
300 : [result] "+r" (result)
301 : [i] "r" (i), [scale] "r" (scale)
307 #error "No implementation for scale16by8 available."
311 /// scale a 16-bit unsigned value by a 16-bit value,
312 /// considered as numerator of a fraction whose denominator
313 /// is 65536. In other words, it computes i * (scale / 65536)
315 LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale )
319 #if FASTLED_SCALE8_FIXED == 1
320 result = ((uint32_t)(i) * (1+(uint32_t)(scale))) / 65536;
322 result = ((uint32_t)(i) * (uint32_t)(scale)) / 65536;
325 #elif SCALE16_AVRASM == 1
326 #if FASTLED_SCALE8_FIXED == 1
327 // implemented sort of like
328 // result = ((i * scale) + i ) / 65536
330 // why not like this, you may ask?
331 // result = (i * (scale+1)) / 65536
332 // the answer is that if scale is 65535, then scale+1
333 // will be zero, which is not what we want.
336 // result.A-B = i.A x scale.A
337 " mul %A[i], %A[scale] \n\t"
340 //" mov %A[result], r0 \n\t"
341 //" mov %B[result], r1 \n\t"
342 // which can be written as...
343 " movw %A[result], r0 \n\t"
344 // Because we're going to add i.A-B to
345 // result.A-D, we DO need to keep both
346 // the r0 and r1 portions of the product
347 // UNlike in the 'unfixed scale8' version.
348 // So the movw here is needed.
349 : [result] "=r" (result)
356 // result.C-D = i.B x scale.B
357 " mul %B[i], %B[scale] \n\t"
358 //" mov %C[result], r0 \n\t"
359 //" mov %D[result], r1 \n\t"
360 " movw %C[result], r0 \n\t"
361 : [result] "+r" (result)
367 const uint8_t zero = 0;
369 // result.B-D += i.B x scale.A
370 " mul %B[i], %A[scale] \n\t"
372 " add %B[result], r0 \n\t"
373 " adc %C[result], r1 \n\t"
374 " adc %D[result], %[zero] \n\t"
376 // result.B-D += i.A x scale.B
377 " mul %A[i], %B[scale] \n\t"
379 " add %B[result], r0 \n\t"
380 " adc %C[result], r1 \n\t"
381 " adc %D[result], %[zero] \n\t"
386 : [result] "+r" (result)
394 // result.A-D += i.A-B
395 " add %A[result], %A[i] \n\t"
396 " adc %B[result], %B[i] \n\t"
397 " adc %C[result], %[zero] \n\t"
398 " adc %D[result], %[zero] \n\t"
399 : [result] "+r" (result)
404 result = result >> 16;
409 // result.A-B = i.A x scale.A
410 " mul %A[i], %A[scale] \n\t"
413 //" mov %A[result], r0 \n\t"
414 //" mov %B[result], r1 \n\t"
415 // which can be written as...
416 " movw %A[result], r0 \n\t"
417 // We actually don't need to do anything with r0,
418 // as result.A is never used again here, so we
419 // could just move the high byte, but movw is
420 // one clock cycle, just like mov, so might as
421 // well, in case we want to use this code for
422 // a generic 16x16 multiply somewhere.
424 : [result] "=r" (result)
431 // result.C-D = i.B x scale.B
432 " mul %B[i], %B[scale] \n\t"
433 //" mov %C[result], r0 \n\t"
434 //" mov %D[result], r1 \n\t"
435 " movw %C[result], r0 \n\t"
436 : [result] "+r" (result)
442 const uint8_t zero = 0;
444 // result.B-D += i.B x scale.A
445 " mul %B[i], %A[scale] \n\t"
447 " add %B[result], r0 \n\t"
448 " adc %C[result], r1 \n\t"
449 " adc %D[result], %[zero] \n\t"
451 // result.B-D += i.A x scale.B
452 " mul %A[i], %B[scale] \n\t"
454 " add %B[result], r0 \n\t"
455 " adc %C[result], r1 \n\t"
456 " adc %D[result], %[zero] \n\t"
461 : [result] "+r" (result)
468 result = result >> 16;
472 #error "No implementation for scale16 available."
477 ///@defgroup Dimming Dimming and brightening functions
479 /// Dimming and brightening functions
481 /// The eye does not respond in a linear way to light.
482 /// High speed PWM'd LEDs at 50% duty cycle appear far
483 /// brighter then the 'half as bright' you might expect.
485 /// If you want your midpoint brightness leve (128) to
486 /// appear half as bright as 'full' brightness (255), you
487 /// have to apply a 'dimming function'.
490 /// Adjust a scaling value for dimming
491 LIB8STATIC uint8_t dim8_raw( uint8_t x)
493 return scale8( x, x);
496 /// Adjust a scaling value for dimming for video (value will never go below 1)
497 LIB8STATIC uint8_t dim8_video( uint8_t x)
499 return scale8_video( x, x);
502 /// Linear version of the dimming function that halves for values < 128
503 LIB8STATIC uint8_t dim8_lin( uint8_t x )
514 /// inverse of the dimming function, brighten a value
515 LIB8STATIC uint8_t brighten8_raw( uint8_t x)
517 uint8_t ix = 255 - x;
518 return 255 - scale8( ix, ix);
521 /// inverse of the dimming function, brighten a value
522 LIB8STATIC uint8_t brighten8_video( uint8_t x)
524 uint8_t ix = 255 - x;
525 return 255 - scale8_video( ix, ix);
528 /// inverse of the dimming function, brighten a value
529 LIB8STATIC uint8_t brighten8_lin( uint8_t x )
531 uint8_t ix = 255 - x;
533 ix = scale8( ix, ix);