diff options
author | Daniel Garcia <danielgarcia@gmail.com> | 2016-01-23 22:15:34 +0300 |
---|---|---|
committer | Daniel Garcia <danielgarcia@gmail.com> | 2016-01-23 22:15:34 +0300 |
commit | 45b804fe04c3e02ba85d5eb63e9daf4d83dd432b (patch) | |
tree | 3b145fd5a0291a45855b3f35174f79c555680916 | |
parent | 92928ce02cba8183209bbfcf022f4622b6cd84d6 (diff) | |
parent | 6d6aca7330c4c2e1b29cf791dbf2550692da59d2 (diff) |
Merge branch 'master' into rgbw
40 files changed, 1590 insertions, 167 deletions
diff --git a/FastLED.cpp b/FastLED.cpp index af92429e..949372ea 100644 --- a/FastLED.cpp +++ b/FastLED.cpp @@ -114,14 +114,18 @@ void CFastLED::clearData() { void CFastLED::delay(unsigned long ms) { unsigned long start = millis(); - while((millis()-start) < ms) { + do { #ifndef FASTLED_ACCURATE_CLOCK // make sure to allow at least one ms to pass to ensure the clock moves // forward ::delay(1); #endif show(); +#if defined(ARDUINO) && (ARDUINO > 150) + yield(); +#endif } + while((millis()-start) < ms); } void CFastLED::setTemperature(const struct CRGB & temp) { @@ -45,8 +45,10 @@ #include "fastled_progmem.h" #include "lib8tion.h" +#include "pixeltypes.h" #include "hsv2rgb.h" #include "colorutils.h" +#include "pixelset.h" #include "colorpalettes.h" #include "noise.h" @@ -85,9 +87,11 @@ template<uint8_t DATA_PIN, EOrder RGB_ORDER> class TM1803 : public TM1803Control template<uint8_t DATA_PIN, EOrder RGB_ORDER> class UCS1903 : public UCS1903Controller400Khz<DATA_PIN, RGB_ORDER> {}; template<uint8_t DATA_PIN, EOrder RGB_ORDER> class UCS1903B : public UCS1903BController800Khz<DATA_PIN, RGB_ORDER> {}; template<uint8_t DATA_PIN, EOrder RGB_ORDER> class UCS1904 : public UCS1904Controller800Khz<DATA_PIN, RGB_ORDER> {}; +template<uint8_t DATA_PIN, EOrder RGB_ORDER> class UCS2903 : public UCS2903Controller<DATA_PIN, RGB_ORDER> {}; template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2812 : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {}; template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2812B : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {}; template<uint8_t DATA_PIN, EOrder RGB_ORDER> class SK6812 : public SK6812Controller<DATA_PIN, RGB_ORDER> {}; +template<uint8_t DATA_PIN, EOrder RGB_ORDER> class SK6822 : public SK6822Controller<DATA_PIN, RGB_ORDER> {}; template<uint8_t DATA_PIN, EOrder RGB_ORDER> class PL9823 : public PL9823Controller<DATA_PIN, RGB_ORDER> {}; template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2811 : public WS2811Controller800Khz<DATA_PIN, RGB_ORDER> {}; template<uint8_t DATA_PIN, EOrder RGB_ORDER> class APA104 : public WS2811Controller800Khz<DATA_PIN, RGB_ORDER> {}; @@ -438,7 +442,8 @@ public: void showColor(const struct CRGB & color) { showColor(color, m_Scale); } /// Delay for the given number of milliseconds. Provided to allow the library to be used on platforms - /// that don't have a delay function (to allow code to be more portable) + /// that don't have a delay function (to allow code to be more portable). Note: this will call show + /// constantly to drive the dithering engine (and will call show at least once). /// @param ms the number of milliseconds to pause for void delay(unsigned long ms); @@ -1,6 +1,7 @@ [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/FastLED/public) -IMPORTANT NOTE: For AVR based systems, avr-gcc 4.8.x is supported, as is avr-gcc 4.3 and earlier. There are known issues with avr-gcc 4.7 and timing based chipsets like the WS2812B. If you are using a linux system make sure you are using avr-gcc 4.8.x not avr-gcc 4.7.x. +IMPORTANT NOTE: For AVR based systems, avr-gcc 4.8.x is supported and tested. This means Arduino 1.6.5 and later. + FastLED 3.1 =========== @@ -1,6 +1,8 @@ #ifndef __INC_BITSWAP_H #define __INC_BITSWAP_H +#include "FastLED.h" + FASTLED_NAMESPACE_BEGIN ///@file bitswap.h @@ -1,6 +1,7 @@ #ifndef __INC_CHIPSETS_H #define __INC_CHIPSETS_H +#include "FastLED.h" #include "pixeltypes.h" ///@file chipsets.h @@ -333,6 +334,9 @@ template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class WS2811Controller400Khz : public ClocklessController<DATA_PIN, 4 * FMUL, 10 * FMUL, 6 * FMUL, RGB_ORDER> {}; template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> +class SK6822Controller : public ClocklessController<DATA_PIN, 3 * FMUL, 8 * FMUL, 3 * FMUL, RGB_ORDER> {}; + +template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class SK6812Controller : public ClocklessController<DATA_PIN, 3 * FMUL, 3 * FMUL, 4 * FMUL, RGB_ORDER> {}; template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> @@ -345,6 +349,9 @@ template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class UCS1904Controller800Khz : public ClocklessController<DATA_PIN, 3 * FMUL, 3 * FMUL, 4 * FMUL, RGB_ORDER> {}; template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> +class UCS2903Controller : public ClocklessController<DATA_PIN, 2 * FMUL, 6 * FMUL, 2 * FMUL, RGB_ORDER> {}; + +template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class TM1809Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER> {}; template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> @@ -383,6 +390,10 @@ class UCS1903BController800Khz : public ClocklessController<DATA_PIN, NS(400), N template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class UCS1904Controller800Khz : public ClocklessController<DATA_PIN, NS(400), NS(400), NS(450), RGB_ORDER> {}; +// UCS2903 - 250ns, 750ns, 250ns +template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> +class UCS2903Controller : public ClocklessController<DATA_PIN, NS(250), NS(750), NS(250), RGB_ORDER> {}; + // TM1809 - 350ns, 350ns, 550ns template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class TM1809Controller800Khz : public ClocklessController<DATA_PIN, NS(350), NS(350), NS(450), RGB_ORDER> {}; @@ -413,6 +424,9 @@ template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class LPD1886Controller1250Khz : public ClocklessController<DATA_PIN, NS(200), NS(400), NS(200), RGB_ORDER, 4> {}; template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> +class SK6822Controller : public ClocklessController<DATA_PIN, NS(375), NS(1000), NS(375), RGB_ORDER> {}; + +template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class SK6812Controller : public ClocklessController<DATA_PIN, NS(300), NS(300), NS(600), RGB_ORDER> {}; template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> @@ -1,6 +1,8 @@ #ifndef __INC_COLOR_H #define __INC_COLOR_H +#include "FastLED.h" + FASTLED_NAMESPACE_BEGIN ///@file color.h diff --git a/colorpalettes.h b/colorpalettes.h index 97f0cb5d..4458575e 100644 --- a/colorpalettes.h +++ b/colorpalettes.h @@ -1,6 +1,7 @@ #ifndef __INC_COLORPALETTES_H #define __INC_COLORPALETTES_H +#include "FastLED.h" #include "colorutils.h" ///@file colorpalettes.h diff --git a/colorutils.cpp b/colorutils.cpp index 5608cf85..80c694af 100644 --- a/colorutils.cpp +++ b/colorutils.cpp @@ -475,70 +475,103 @@ CRGB HeatColor( uint8_t temperature) } +// lsrX4: helper function to divide a number by 16, aka four LSR's. +// On avr-gcc, "u8 >> 4" generates a loop, which is big, and slow. +// merely forcing it to be four /=2's causes avr-gcc to emit +// a SWAP instruction followed by an AND 0x0F, which is faster, and smaller. +inline uint8_t lsrX4( uint8_t dividend) __attribute__((always_inline)); +inline uint8_t lsrX4( uint8_t dividend) +{ +#if defined(__AVR__) + dividend /= 2; + dividend /= 2; + dividend /= 2; + dividend /= 2; +#else + dividend >>= 4; +#endif + return dividend; +} + CRGB ColorFromPalette( const CRGBPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType) { - uint8_t hi4 = index >> 4; + // hi4 = index >> 4; + uint8_t hi4 = lsrX4(index); uint8_t lo4 = index & 0x0F; - - // CRGB rgb1 = pal[ hi4]; - const CRGB* entry = &(pal[0]) + hi4; + + // const CRGB* entry = &(pal[0]) + hi4; + // since hi4 is always 0..15, hi4 * sizeof(CRGB) can be a single-byte value, + // instead of the two byte 'int' that avr-gcc defaults to. + // So, we multiply hi4 X sizeof(CRGB), giving hi4XsizeofCRGB; + uint8_t hi4XsizeofCRGB = hi4 * sizeof(CRGB); + // We then add that to a base array pointer. + const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + hi4XsizeofCRGB); + + uint8_t blend = lo4 && (blendType != NOBLEND); + uint8_t red1 = entry->red; uint8_t green1 = entry->green; uint8_t blue1 = entry->blue; - - uint8_t blend = lo4 && (blendType != NOBLEND); - + + if( blend ) { - + if( hi4 == 15 ) { entry = &(pal[0]); } else { entry++; } - + uint8_t f2 = lo4 << 4; uint8_t f1 = 255 - f2; - + // rgb1.nscale8(f1); + uint8_t red2 = entry->red; red1 = scale8_LEAVING_R1_DIRTY( red1, f1); - green1 = scale8_LEAVING_R1_DIRTY( green1, f1); - blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); - - // cleanup_R1(); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; - // CRGB rgb2 = pal[ hi4]; - // rgb2.nscale8(f2); - uint8_t red2 = entry->red; uint8_t green2 = entry->green; - uint8_t blue2 = entry->blue; - red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); green2 = scale8_LEAVING_R1_DIRTY( green2, f2); - blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); - - cleanup_R1(); - - // These sums can't overflow, so no qadd8 needed. - red1 += red2; green1 += green2; - blue1 += blue2; + uint8_t blue2 = entry->blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; + + cleanup_R1(); } - + if( brightness != 255) { - nscale8x3_video( red1, green1, blue1, brightness); + if( brightness ) { + brightness++; // adjust for rounding + // Now, since brightness is nonzero, we don't need the full scale8_video logic; + // we can just to scale8 and then add one to all nonzero inputs. + if( red1 ) { red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); red1++; } + if( green1 ) { green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); green1++; } + if( blue1 ) { blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); blue1++; } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } } - + return CRGB( red1, green1, blue1); } CRGB ColorFromPalette( const TProgmemRGBPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType) { - uint8_t hi4 = index >> 4; + // hi4 = index >> 4; + uint8_t hi4 = lsrX4(index); uint8_t lo4 = index & 0x0F; - // CRGB rgb1 = pal[ hi4]; CRGB entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) + hi4 ); + uint8_t red1 = entry.red; uint8_t green1 = entry.green; @@ -557,35 +590,185 @@ CRGB ColorFromPalette( const TProgmemRGBPalette16& pal, uint8_t index, uint8_t b uint8_t f2 = lo4 << 4; uint8_t f1 = 255 - f2; - // rgb1.nscale8(f1); + uint8_t red2 = entry.red; red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry.green; green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry.blue; blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; - // cleanup_R1(); + cleanup_R1(); + } - // CRGB rgb2 = pal[ hi4]; - // rgb2.nscale8(f2); - uint8_t red2 = entry.red; - uint8_t green2 = entry.green; - uint8_t blue2 = entry.blue; + if( brightness != 255) { + if( brightness ) { + brightness++; // adjust for rounding + // Now, since brightness is nonzero, we don't need the full scale8_video logic; + // we can just to scale8 and then add one to all nonzero inputs. + if( red1 ) { red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); red1++; } + if( green1 ) { green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); green1++; } + if( blue1 ) { blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); blue1++; } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} + + +CRGB ColorFromPalette( const CRGBPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + uint8_t hi5 = index; +#if defined(__AVR__) + hi5 /= 2; + hi5 /= 2; + hi5 /= 2; +#else + hi5 >>= 3; +#endif + uint8_t lo3 = index & 0x07; + + // const CRGB* entry = &(pal[0]) + hi5; + // since hi5 is always 0..31, hi4 * sizeof(CRGB) can be a single-byte value, + // instead of the two byte 'int' that avr-gcc defaults to. + // So, we multiply hi5 X sizeof(CRGB), giving hi5XsizeofCRGB; + uint8_t hi5XsizeofCRGB = hi5 * sizeof(CRGB); + // We then add that to a base array pointer. + const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + hi5XsizeofCRGB); + + uint8_t red1 = entry->red; + uint8_t green1 = entry->green; + uint8_t blue1 = entry->blue; + + uint8_t blend = lo3 && (blendType != NOBLEND); + + if( blend ) { + + if( hi5 == 31 ) { + entry = &(pal[0]); + } else { + entry++; + } + + uint8_t f2 = lo3 << 5; + uint8_t f1 = 255 - f2; + + uint8_t red2 = entry->red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry->green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry->blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; cleanup_R1(); + + } + + if( brightness != 255) { + if( brightness ) { + brightness++; // adjust for rounding + // Now, since brightness is nonzero, we don't need the full scale8_video logic; + // we can just to scale8 and then add one to all nonzero inputs. + if( red1 ) { red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); red1++; } + if( green1 ) { green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); green1++; } + if( blue1 ) { blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); blue1++; } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} - // These sums can't overflow, so no qadd8 needed. + +CRGB ColorFromPalette( const TProgmemRGBPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + uint8_t hi5 = index; +#if defined(__AVR__) + hi5 /= 2; + hi5 /= 2; + hi5 /= 2; +#else + hi5 >>= 3; +#endif + uint8_t lo3 = index & 0x07; + + CRGB entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) + hi5); + + uint8_t red1 = entry.red; + uint8_t green1 = entry.green; + uint8_t blue1 = entry.blue; + + uint8_t blend = lo3 && (blendType != NOBLEND); + + if( blend ) { + + if( hi5 == 31 ) { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) ); + } else { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[1]) + hi5 ); + } + + uint8_t f2 = lo3 << 5; + uint8_t f1 = 255 - f2; + + uint8_t red2 = entry.red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); red1 += red2; + + uint8_t green2 = entry.green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); green1 += green2; + + uint8_t blue2 = entry.blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); blue1 += blue2; - + + cleanup_R1(); } - + if( brightness != 255) { - nscale8x3_video( red1, green1, blue1, brightness); + if( brightness ) { + brightness++; // adjust for rounding + // Now, since brightness is nonzero, we don't need the full scale8_video logic; + // we can just to scale8 and then add one to all nonzero inputs. + if( red1 ) { red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); red1++; } + if( green1 ) { green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); green1++; } + if( blue1 ) { blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); blue1++; } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } } - + return CRGB( red1, green1, blue1); } @@ -600,7 +783,11 @@ CRGB ColorFromPalette( const CRGBPalette256& pal, uint8_t index, uint8_t brightn uint8_t blue = entry->blue; if( brightness != 255) { - nscale8x3_video( red, green, blue, brightness); + brightness++; // adjust for rounding + red = scale8_video_LEAVING_R1_DIRTY( red, brightness); + green = scale8_video_LEAVING_R1_DIRTY( green, brightness); + blue = scale8_video_LEAVING_R1_DIRTY( blue, brightness); + cleanup_R1(); } return CRGB( red, green, blue); @@ -609,7 +796,8 @@ CRGB ColorFromPalette( const CRGBPalette256& pal, uint8_t index, uint8_t brightn CHSV ColorFromPalette( const struct CHSVPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType) { - uint8_t hi4 = index >> 4; + // hi4 = index >> 4; + uint8_t hi4 = lsrX4(index); uint8_t lo4 = index & 0x0F; // CRGB rgb1 = pal[ hi4]; @@ -690,6 +878,95 @@ CHSV ColorFromPalette( const struct CHSVPalette16& pal, uint8_t index, uint8_t b } +CHSV ColorFromPalette( const struct CHSVPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + uint8_t hi5 = index; +#if defined(__AVR__) + hi5 /= 2; + hi5 /= 2; + hi5 /= 2; +#else + hi5 >>= 3; +#endif + uint8_t lo3 = index & 0x07; + + uint8_t hi5XsizeofCHSV = hi5 * sizeof(CHSV); + const CHSV* entry = (CHSV*)( (uint8_t*)(&(pal[0])) + hi5XsizeofCHSV); + + uint8_t hue1 = entry->hue; + uint8_t sat1 = entry->sat; + uint8_t val1 = entry->val; + + uint8_t blend = lo3 && (blendType != NOBLEND); + + if( blend ) { + + if( hi5 == 31 ) { + entry = &(pal[0]); + } else { + entry++; + } + + uint8_t f2 = lo3 << 5; + uint8_t f1 = 255 - f2; + + uint8_t hue2 = entry->hue; + uint8_t sat2 = entry->sat; + uint8_t val2 = entry->val; + + // Now some special casing for blending to or from + // either black or white. Black and white don't have + // proper 'hue' of their own, so when ramping from + // something else to/from black/white, we set the 'hue' + // of the black/white color to be the same as the hue + // of the other color, so that you get the expected + // brightness or saturation ramp, with hue staying + // constant: + + // If we are starting from white (sat=0) + // or black (val=0), adopt the target hue. + if( sat1 == 0 || val1 == 0) { + hue1 = hue2; + } + + // If we are ending at white (sat=0) + // or black (val=0), adopt the starting hue. + if( sat2 == 0 || val2 == 0) { + hue2 = hue1; + } + + + sat1 = scale8_LEAVING_R1_DIRTY( sat1, f1); + val1 = scale8_LEAVING_R1_DIRTY( val1, f1); + + sat2 = scale8_LEAVING_R1_DIRTY( sat2, f2); + val2 = scale8_LEAVING_R1_DIRTY( val2, f2); + + // cleanup_R1(); + + // These sums can't overflow, so no qadd8 needed. + sat1 += sat2; + val1 += val2; + + uint8_t deltaHue = (uint8_t)(hue2 - hue1); + if( deltaHue & 0x80 ) { + // go backwards + hue1 -= scale8( 255 - deltaHue, f2); + } else { + // go forwards + hue1 += scale8( deltaHue, f2); + } + + cleanup_R1(); + } + + if( brightness != 255) { + val1 = scale8_video( val1, brightness); + } + + return CHSV( hue1, sat1, val1); +} + CHSV ColorFromPalette( const struct CHSVPalette256& pal, uint8_t index, uint8_t brightness, TBlendType) { CHSV hsv = *( &(pal[0]) + index ); @@ -716,6 +993,41 @@ void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette256& } } + +void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette32& destpal32) +{ + for( uint8_t i = 0; i < 16; i++) { + uint8_t j = i * 2; + destpal32[j+0] = srcpal16[i]; + destpal32[j+1] = srcpal16[i]; + } +} + +void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette32& destpal32) +{ + for( uint8_t i = 0; i < 16; i++) { + uint8_t j = i * 2; + destpal32[j+0] = srcpal16[i]; + destpal32[j+1] = srcpal16[i]; + } +} + +void UpscalePalette(const struct CRGBPalette32& srcpal32, struct CRGBPalette256& destpal256) +{ + for( int i = 0; i < 256; i++) { + destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal32, i); + } +} + +void UpscalePalette(const struct CHSVPalette32& srcpal32, struct CHSVPalette256& destpal256) +{ + for( int i = 0; i < 256; i++) { + destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal32, i); + } +} + + + #if 0 // replaced by PartyColors_p void SetupPartyColors(CRGBPalette16& pal) diff --git a/colorutils.h b/colorutils.h index f27e0f4c..18a052fb 100644 --- a/colorutils.h +++ b/colorutils.h @@ -4,6 +4,7 @@ ///@file colorutils.h /// functions for color fill, paletters, blending, and more +#include "FastLED.h" #include "pixeltypes.h" #include "fastled_progmem.h" @@ -391,12 +392,17 @@ CRGB HeatColor( uint8_t temperature); // class CRGBPalette16; +class CRGBPalette32; class CRGBPalette256; class CHSVPalette16; +class CHSVPalette32; class CHSVPalette256; typedef uint32_t TProgmemRGBPalette16[16]; typedef uint32_t TProgmemHSVPalette16[16]; #define TProgmemPalette16 TProgmemRGBPalette16 +typedef uint32_t TProgmemRGBPalette32[32]; +typedef uint32_t TProgmemHSVPalette32[32]; +#define TProgmemPalette32 TProgmemRGBPalette32 typedef const uint8_t TProgmemRGBGradientPalette_byte ; typedef const TProgmemRGBGradientPalette_byte *TProgmemRGBGradientPalette_bytes; @@ -420,6 +426,14 @@ typedef TDynamicRGBGradientPalette_bytes TDynamicRGBGradientPalettePtr; void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette256& destpal256); void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette256& destpal256); +// Convert a 16-entry palette to a 32-entry palette +void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette32& destpal32); +void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette32& destpal32); + +// Convert a 32-entry palette to a 256-entry palette +void UpscalePalette(const struct CRGBPalette32& srcpal32, struct CRGBPalette256& destpal256); +void UpscalePalette(const struct CHSVPalette32& srcpal32, struct CHSVPalette256& destpal256); + class CHSVPalette16 { public: @@ -648,11 +662,20 @@ public: { memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } + CRGBPalette16( const CRGB rhs[16]) + { + memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + } CRGBPalette16& operator=( const CRGBPalette16& rhs) { memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } + CRGBPalette16& operator=( const CRGB rhs[16]) + { + memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + return *this; + } CRGBPalette16( const CHSVPalette16& rhs) { @@ -660,6 +683,12 @@ public: entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } } + CRGBPalette16( const CHSV rhs[16]) + { + for( uint8_t i = 0; i < 16; i++) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + } CRGBPalette16& operator=( const CHSVPalette16& rhs) { for( uint8_t i = 0; i < 16; i++) { @@ -667,6 +696,13 @@ public: } return *this; } + CRGBPalette16& operator=( const CHSV rhs[16]) + { + for( uint8_t i = 0; i < 16; i++) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + return *this; + } CRGBPalette16( const TProgmemRGBPalette16& rhs) { @@ -871,6 +907,405 @@ public: }; + + +class CHSVPalette32 { +public: + CHSV entries[32]; + CHSVPalette32() {}; + CHSVPalette32( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, + const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, + const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, + const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 ) + { + for( uint8_t i = 0; i < 2; i++) { + entries[0+i]=c00; entries[2+i]=c01; entries[4+i]=c02; entries[6+i]=c03; + entries[8+i]=c04; entries[10+i]=c05; entries[12+i]=c06; entries[14+i]=c07; + entries[16+i]=c08; entries[18+i]=c09; entries[20+i]=c10; entries[22+i]=c11; + entries[24+i]=c12; entries[26+i]=c13; entries[28+i]=c14; entries[30+i]=c15; + } + }; + + CHSVPalette32( const CHSVPalette32& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + CHSVPalette32& operator=( const CHSVPalette32& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + + CHSVPalette32( const TProgmemHSVPalette32& rhs) + { + for( uint8_t i = 0; i < 32; i++) { + CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); + entries[i].hue = xyz.red; + entries[i].sat = xyz.green; + entries[i].val = xyz.blue; + } + } + CHSVPalette32& operator=( const TProgmemHSVPalette32& rhs) + { + for( uint8_t i = 0; i < 32; i++) { + CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); + entries[i].hue = xyz.red; + entries[i].sat = xyz.green; + entries[i].val = xyz.blue; + } + return *this; + } + + inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + inline CHSV& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + inline const CHSV& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + operator CHSV*() + { + return &(entries[0]); + } + + bool operator==( const CHSVPalette32 rhs) + { + const uint8_t* p = (const uint8_t*)(&(this->entries[0])); + const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); + if( p == q) return true; + for( uint8_t i = 0; i < (sizeof( entries)); i++) { + if( *p != *q) return false; + p++; + q++; + } + return true; + } + bool operator!=( const CHSVPalette32 rhs) + { + return !( *this == rhs); + } + + CHSVPalette32( const CHSV& c1) + { + fill_solid( &(entries[0]), 32, c1); + } + CHSVPalette32( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 32, c1, c2); + } + CHSVPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 32, c1, c2, c3); + } + CHSVPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 32, c1, c2, c3, c4); + } + +}; + +class CRGBPalette32 { +public: + CRGB entries[32]; + CRGBPalette32() {}; + CRGBPalette32( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03, + const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07, + const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11, + const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 ) + { + for( uint8_t i = 0; i < 2; i++) { + entries[0+i]=c00; entries[2+i]=c01; entries[4+i]=c02; entries[6+i]=c03; + entries[8+i]=c04; entries[10+i]=c05; entries[12+i]=c06; entries[14+i]=c07; + entries[16+i]=c08; entries[18+i]=c09; entries[20+i]=c10; entries[22+i]=c11; + entries[24+i]=c12; entries[26+i]=c13; entries[28+i]=c14; entries[30+i]=c15; + } + }; + + CRGBPalette32( const CRGBPalette32& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + CRGBPalette32( const CRGB rhs[32]) + { + memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + } + CRGBPalette32& operator=( const CRGBPalette32& rhs) + { + memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + CRGBPalette32& operator=( const CRGB rhs[32]) + { + memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + return *this; + } + + CRGBPalette32( const CHSVPalette32& rhs) + { + for( uint8_t i = 0; i < 32; i++) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + } + CRGBPalette32( const CHSV rhs[32]) + { + for( uint8_t i = 0; i < 32; i++) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + } + CRGBPalette32& operator=( const CHSVPalette32& rhs) + { + for( uint8_t i = 0; i < 32; i++) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + CRGBPalette32& operator=( const CHSV rhs[32]) + { + for( uint8_t i = 0; i < 32; i++) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + + CRGBPalette32( const TProgmemRGBPalette32& rhs) + { + for( uint8_t i = 0; i < 32; i++) { + entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); + } + } + CRGBPalette32& operator=( const TProgmemRGBPalette32& rhs) + { + for( uint8_t i = 0; i < 32; i++) { + entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); + } + return *this; + } + + bool operator==( const CRGBPalette32 rhs) + { + const uint8_t* p = (const uint8_t*)(&(this->entries[0])); + const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); + if( p == q) return true; + for( uint8_t i = 0; i < (sizeof( entries)); i++) { + if( *p != *q) return false; + p++; + q++; + } + return true; + } + bool operator!=( const CRGBPalette32 rhs) + { + return !( *this == rhs); + } + + inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + inline CRGB& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + inline const CRGB& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + operator CRGB*() + { + return &(entries[0]); + } + + CRGBPalette32( const CHSV& c1) + { + fill_solid( &(entries[0]), 32, c1); + } + CRGBPalette32( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 32, c1, c2); + } + CRGBPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 32, c1, c2, c3); + } + CRGBPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 32, c1, c2, c3, c4); + } + + CRGBPalette32( const CRGB& c1) + { + fill_solid( &(entries[0]), 32, c1); + } + CRGBPalette32( const CRGB& c1, const CRGB& c2) + { + fill_gradient_RGB( &(entries[0]), 32, c1, c2); + } + CRGBPalette32( const CRGB& c1, const CRGB& c2, const CRGB& c3) + { + fill_gradient_RGB( &(entries[0]), 32, c1, c2, c3); + } + CRGBPalette32( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) + { + fill_gradient_RGB( &(entries[0]), 32, c1, c2, c3, c4); + } + + + CRGBPalette32( const CRGBPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + } + CRGBPalette32& operator=( const CRGBPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + return *this; + } + + CRGBPalette32( const TProgmemRGBPalette16& rhs) + { + CRGBPalette16 p16(rhs); + *this = p16; + } + CRGBPalette32& operator=( const TProgmemRGBPalette16& rhs) + { + CRGBPalette16 p16(rhs); + *this = p16; + return *this; + } + + + // Gradient palettes are loaded into CRGB16Palettes in such a way + // that, if possible, every color represented in the gradient palette + // is also represented in the CRGBPalette32. + // For example, consider a gradient palette that is all black except + // for a single, one-element-wide (1/256th!) spike of red in the middle: + // 0, 0,0,0 + // 124, 0,0,0 + // 125, 255,0,0 // one 1/256th-palette-wide red stripe + // 126, 0,0,0 + // 255, 0,0,0 + // A naive conversion of this 256-element palette to a 16-element palette + // might accidentally completely eliminate the red spike, rendering the + // palette completely black. + // However, the conversions provided here would attempt to include a + // the red stripe in the output, more-or-less as faithfully as possible. + // So in this case, the resulting CRGBPalette32 palette would have a red + // stripe in the middle which was 1/16th of a palette wide -- the + // narrowest possible in a CRGBPalette32. + // This means that the relative width of stripes in a CRGBPalette32 + // will be, by definition, different from the widths in the gradient + // palette. This code attempts to preserve "all the colors", rather than + // the exact stripe widths at the expense of dropping some colors. + CRGBPalette32( TProgmemRGBGradientPalette_bytes progpal ) + { + *this = progpal; + } + CRGBPalette32& operator=( TProgmemRGBGradientPalette_bytes progpal ) + { + TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint8_t count = 0; + do { + u.dword = FL_PGM_READ_DWORD_NEAR(progent + count); + count++;; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t iend8 = 0; + while( indexstart < 255) { + progent++; + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + istart8 = indexstart / 8; + iend8 = indexend / 8; + if( count < 16) { + if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 31)) { + istart8 = lastSlotUsed + 1; + if( iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + CRGBPalette32& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) + { + TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint8_t count = 0; + do { + u = *(ent + count); + count++;; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + + u = *ent; + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t iend8 = 0; + while( indexstart < 255) { + ent++; + u = *ent; + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + istart8 = indexstart / 8; + iend8 = indexend / 8; + if( count < 16) { + if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 31)) { + istart8 = lastSlotUsed + 1; + if( iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + +}; + + + class CRGBPalette256 { public: CRGB entries[256]; @@ -889,11 +1324,20 @@ public: { memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } + CRGBPalette256( const CRGB rhs[256]) + { + memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + } CRGBPalette256& operator=( const CRGBPalette256& rhs) { memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } + CRGBPalette256& operator=( const CRGB rhs[256]) + { + memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + return *this; + } CRGBPalette256( const CHSVPalette256& rhs) { @@ -901,6 +1345,12 @@ public: entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } } + CRGBPalette256( const CHSV rhs[256]) + { + for( int i = 0; i < 256; i++) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + } CRGBPalette256& operator=( const CHSVPalette256& rhs) { for( int i = 0; i < 256; i++) { @@ -908,6 +1358,13 @@ public: } return *this; } + CRGBPalette256& operator=( const CHSV rhs[256]) + { + for( int i = 0; i < 256; i++) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + return *this; + } CRGBPalette256( const CRGBPalette16& rhs16) { @@ -1051,7 +1508,6 @@ public: - typedef enum { NOBLEND=0, LINEARBLEND=1 } TBlendType; CRGB ColorFromPalette( const CRGBPalette16& pal, @@ -1079,6 +1535,21 @@ CHSV ColorFromPalette( const CHSVPalette256& pal, uint8_t brightness=255, TBlendType blendType=NOBLEND ); +CRGB ColorFromPalette( const CRGBPalette32& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + +CRGB ColorFromPalette( const TProgmemRGBPalette32& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + +CHSV ColorFromPalette( const CHSVPalette32& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + // Fill a range of LEDs with a sequece of entryies from a palette template <typename PALETTE> diff --git a/controller.h b/controller.h index bb063cdf..06d3c47e 100644 --- a/controller.h +++ b/controller.h @@ -4,6 +4,7 @@ ///@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" diff --git a/cpp_compat.h b/cpp_compat.h index 94674170..ab5b773a 100644 --- a/cpp_compat.h +++ b/cpp_compat.h @@ -1,6 +1,8 @@ #ifndef __INC_CPP_COMPAT_H #define __INC_CPP_COMPAT_H +#include "FastLED.h" + #if __cplusplus <= 199711L #define static_assert(expression, message) @@ -1,6 +1,7 @@ #ifndef __INC_DMX_H #define __INC_DMX_H +#include "FastLED.h" #ifdef DmxSimple_h #include<DmxSimple.h> diff --git a/docs/.Doxyfile.swp b/docs/.Doxyfile.swp Binary files differdeleted file mode 100644 index d223ba7d..00000000 --- a/docs/.Doxyfile.swp +++ /dev/null diff --git a/examples/DemoReel100/DemoReel100.ino b/examples/DemoReel100/DemoReel100.ino index 0f57cad7..ffa3a9fd 100644 --- a/examples/DemoReel100/DemoReel100.ino +++ b/examples/DemoReel100/DemoReel100.ino @@ -11,7 +11,7 @@ FASTLED_USING_NAMESPACE // // -Mark Kriegsman, December 2014 -#if FASTLED_VERSION < 3001000 +#if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3001000) #warning "Requires FastLED 3.1 or later; check github for latest code." #endif diff --git a/examples/RGBSetDemo/RGBSetDemo.ino b/examples/RGBSetDemo/RGBSetDemo.ino new file mode 100644 index 00000000..3b9ef24e --- /dev/null +++ b/examples/RGBSetDemo/RGBSetDemo.ino @@ -0,0 +1,22 @@ +#include<FastLED.h> +#define NUM_LEDS 40 + +CRGBArray<NUM_LEDS> leds; + +void setup() { FastLED.addLeds<NEOPIXEL,6>(leds, NUM_LEDS); } + +void loop(){ + static uint8_t hue; + for(int i = 0; i < NUM_LEDS/2; i++) { + // fade everything out + leds.fadeToBlackBy(40); + + // let's set an led value + leds[i] = CHSV(hue++,255,255); + + // now, let's first 20 leds to the top 20 leds, + leds(NUM_LEDS/2,NUM_LEDS-1) = leds(NUM_LEDS/2 - 1 ,0); + FastLED.delay(33); + } +} + diff --git a/examples/XYMatrix/XYMatrix.ino b/examples/XYMatrix/XYMatrix.ino index ff0c5d14..53c21411 100644 --- a/examples/XYMatrix/XYMatrix.ino +++ b/examples/XYMatrix/XYMatrix.ino @@ -121,7 +121,7 @@ uint16_t XY( uint8_t x, uint8_t y) // it, "leds_plus_safety_pixel". Then declare "leds" as a pointer to // that array, but starting with the 2nd element (id=1) of that array: // CRGB leds_with_safety_pixel[41]; -// const CRGB* leds( leds_plus_safety_pixel + 1); +// CRGB* const leds( leds_plus_safety_pixel + 1); // Then you use the "leds" array as you normally would. // Now "leds[0..N]" are aliases for "leds_plus_safety_pixel[1..(N+1)]", // AND leds[-1] is now a legitimate and safe alias for leds_plus_safety_pixel[0]. @@ -150,7 +150,7 @@ uint16_t XY( uint8_t x, uint8_t y) #define NUM_LEDS (kMatrixWidth * kMatrixHeight) CRGB leds_plus_safety_pixel[ NUM_LEDS + 1]; -CRGB* leds( leds_plus_safety_pixel + 1); +CRGB* const leds( leds_plus_safety_pixel + 1); uint16_t XYsafe( uint8_t x, uint8_t y) { @@ -194,4 +194,3 @@ void setup() { FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalSMD5050); FastLED.setBrightness( BRIGHTNESS ); } - diff --git a/fastled_config.h b/fastled_config.h index 42023f53..aa510e58 100644 --- a/fastled_config.h +++ b/fastled_config.h @@ -1,6 +1,8 @@ #ifndef __INC_FASTLED_CONFIG_H #define __INC_FASTLED_CONFIG_H +#include "FastLED.h" + ///@file fastled_config.h /// contains definitions that can be used to configure FastLED at compile time @@ -19,4 +21,12 @@ // #define FASTLED_ALLOW_INTERRUPTS 1 // #define FASTLED_ALLOW_INTERRUPTS 0 +// Use this to allow some integer overflows/underflows in the inoise functions. +// The original implementions allowed this, and had some discontinuties in the noise +// output. It's technically an implementation bug, and was fixed, but you may wish +// to preserve the old look and feel of the inoise functions in your existing animations. +// The default is 0: NO overflow, and 'continuous' noise output, aka the fixed way. +// #define FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW 0 +// #define FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW 1 + #endif diff --git a/fastled_delay.h b/fastled_delay.h index 5df7609d..f16d322e 100644 --- a/fastled_delay.h +++ b/fastled_delay.h @@ -1,6 +1,8 @@ #ifndef __INC_FL_DELAY_H #define __INC_FL_DELAY_H +#include "FastLED.h" + ///@file fastled_delay.h ///Utility functions and classes for managing delaycycles @@ -41,6 +43,11 @@ public: // predeclaration to not upset the compiler template<int CYCLES> inline void delaycycles(); +template<int CYCLES> inline void delaycycles_min1() { + delaycycles<1>(); + delaycycles<CYCLES-1>(); +} + // TODO: ARM version of _delaycycles_ @@ -90,6 +97,10 @@ template<int CYCLES> __attribute__((always_inline)) inline void delaycycles() { // pre-instantiations for values small enough to not need the loop, as well as sanity holders // for some negative values. +template<> __attribute__((always_inline)) inline void delaycycles<-10>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-9>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-8>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-7>() {} template<> __attribute__((always_inline)) inline void delaycycles<-6>() {} template<> __attribute__((always_inline)) inline void delaycycles<-5>() {} template<> __attribute__((always_inline)) inline void delaycycles<-4>() {} diff --git a/fastled_progmem.h b/fastled_progmem.h index f7cedc02..342fe6eb 100644 --- a/fastled_progmem.h +++ b/fastled_progmem.h @@ -1,6 +1,8 @@ #ifndef __INC_FL_PROGMEM_H #define __INC_FL_PROGMEM_H +#include "FastLED.h" + ///@file fastled_progmem.h /// wrapper definitions to allow seamless use of PROGMEM in environmens that have it @@ -1,6 +1,8 @@ #ifndef __INC_FASTPIN_H #define __INC_FASTPIN_H +#include "FastLED.h" + #include "led_sysdefs.h" #pragma GCC diagnostic push @@ -36,8 +38,8 @@ class Pin : public Selectable { void _init() { mPinMask = digitalPinToBitMask(mPin); - mPort = portOutputRegister(digitalPinToPort(mPin)); - mInPort = portInputRegister(digitalPinToPort(mPin)); + mPort = (volatile RwReg*)portOutputRegister(digitalPinToPort(mPin)); + mInPort = (volatile RoReg*)portInputRegister(digitalPinToPort(mPin)); } public: Pin(int pin) : mPin(pin) { _init(); } @@ -1,6 +1,8 @@ #ifndef __INC_FASTSPI_H #define __INC_FASTSPI_H +#include "FastLED.h" + #include "controller.h" #include "lib8tion.h" diff --git a/fastspi_bitbang.h b/fastspi_bitbang.h index 65c2a5d8..6621668a 100644 --- a/fastspi_bitbang.h +++ b/fastspi_bitbang.h @@ -1,6 +1,8 @@ #ifndef __INC_FASTSPI_BITBANG_H #define __INC_FASTSPI_BITBANG_H +#include "FastLED.h" + #include "fastled_delay.h" FASTLED_NAMESPACE_BEGIN @@ -109,20 +111,26 @@ private: } public: - #define SPI_DELAY delaycycles<1+((SPI_SPEED-2) / 2)>(); - #define SPI_DELAY_HALF delaycycles<1+ ((SPI_SPEED-4) / 4)>(); + + // We want to make sure that the clock pulse is held high for a nininum of 35ns. + #define MIN_DELAY (NS(35) - 3) + + #define CLOCK_HI_DELAY delaycycles<MIN_DELAY>(); delaycycles<(((SPI_SPEED-6) / 2) - MIN_DELAY)>(); + #define CLOCK_LO_DELAY delaycycles<(((SPI_SPEED-6) / 4))>(); // write the BIT'th bit out via spi, setting the data pin then strobing the clcok template <uint8_t BIT> __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) { + //cli(); if(b & (1 << BIT)) { FastPin<DATA_PIN>::hi(); - FastPin<CLOCK_PIN>::hi(); SPI_DELAY; - FastPin<CLOCK_PIN>::lo(); SPI_DELAY; + FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY; + FastPin<CLOCK_PIN>::lo(); CLOCK_LO_DELAY; } else { FastPin<DATA_PIN>::lo(); - FastPin<CLOCK_PIN>::hi(); SPI_DELAY; - FastPin<CLOCK_PIN>::lo(); SPI_DELAY; + FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY; + FastPin<CLOCK_PIN>::lo(); CLOCK_LO_DELAY; } + //sei(); } private: @@ -130,12 +138,12 @@ private: template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) { if(b & (1 << BIT)) { FastPin<DATA_PIN>::hi(datapin); - FastPin<CLOCK_PIN>::hi(clockpin); SPI_DELAY; - FastPin<CLOCK_PIN>::lo(clockpin); SPI_DELAY; + FastPin<CLOCK_PIN>::hi(clockpin); CLOCK_HI_DELAY; + FastPin<CLOCK_PIN>::lo(clockpin); CLOCK_LO_DELAY; } else { FastPin<DATA_PIN>::lo(datapin); - FastPin<CLOCK_PIN>::hi(clockpin); SPI_DELAY; - FastPin<CLOCK_PIN>::lo(clockpin); SPI_DELAY; + FastPin<CLOCK_PIN>::hi(clockpin); CLOCK_HI_DELAY; + FastPin<CLOCK_PIN>::lo(clockpin); CLOCK_LO_DELAY; } } @@ -147,13 +155,13 @@ private: // // only need to explicitly set clock hi if clock and data are on different ports if(b & (1 << BIT)) { FastPin<DATA_PIN>::fastset(datapin, hival); - FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); SPI_DELAY; - FastPin<CLOCK_PIN>::fastset(clockpin, loclock); SPI_DELAY; + FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); CLOCK_HI_DELAY; + FastPin<CLOCK_PIN>::fastset(clockpin, loclock); CLOCK_LO_DELAY; } else { // NOP; FastPin<DATA_PIN>::fastset(datapin, loval); - FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); SPI_DELAY; - FastPin<CLOCK_PIN>::fastset(clockpin, loclock); SPI_DELAY; + FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); CLOCK_HI_DELAY; + FastPin<CLOCK_PIN>::fastset(clockpin, loclock); CLOCK_LO_DELAY; } } @@ -166,14 +174,14 @@ private: writeBit<BIT>(b); #else if(b & (1 << BIT)) { - FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); SPI_DELAY_HALF; - FastPin<DATA_PIN>::fastset(clockdatapin, datahiclockhi); SPI_DELAY; - FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); SPI_DELAY_HALF; + FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); + FastPin<DATA_PIN>::fastset(clockdatapin, datahiclockhi); CLOCK_HI_DELAY; + FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); CLOCK_LO_DELAY; } else { // NOP; - FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); SPI_DELAY_HALF; - FastPin<DATA_PIN>::fastset(clockdatapin, dataloclockhi); SPI_DELAY; - FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); SPI_DELAY_HALF; + FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); + FastPin<DATA_PIN>::fastset(clockdatapin, dataloclockhi); CLOCK_HI_DELAY; + FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); CLOCK_LO_DELAY; } #endif } diff --git a/fastspi_nop.h b/fastspi_nop.h index aa363394..5c5da010 100644 --- a/fastspi_nop.h +++ b/fastspi_nop.h @@ -1,6 +1,10 @@ #ifndef __INC_FASTSPI_NOP_H #define __INC_FASTSPI_NOP_H +#if 0 // Guard against the arduino ide idiotically including every header file + +#include "FastLED.h" + FASTLED_NAMESPACE_BEGIN /// A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should @@ -57,3 +61,4 @@ public: FASTLED_NAMESPACE_END #endif +#endif diff --git a/fastspi_ref.h b/fastspi_ref.h index 157ea2e3..f68e63ef 100644 --- a/fastspi_ref.h +++ b/fastspi_ref.h @@ -1,6 +1,9 @@ #ifndef __INC_FASTSPI_ARM_SAM_H #define __INC_FASTSPI_ARM_SAM_H +#if 0 // guard against the arduino ide idiotically including every header file +#include "FastLED.h" + FASTLED_NAMESPACE_BEGIN // A skeletal implementation of hardware SPI support. Fill in the necessary code for init, waiting, and writing. The rest of @@ -87,3 +90,6 @@ public: FASTLED_NAMESPACE_END #endif + +#endif + diff --git a/fastspi_types.h b/fastspi_types.h index 955ac938..5510bba8 100644 --- a/fastspi_types.h +++ b/fastspi_types.h @@ -1,6 +1,8 @@ #ifndef __INC_FASTSPI_TYPES_H #define __INC_FASTSPI_TYPES_H +#include "FastLED.h" + FASTLED_NAMESPACE_BEGIN // Some helper macros for getting at mis-ordered byte values diff --git a/hsv2rgb.cpp b/hsv2rgb.cpp index 17c20c4c..49056d51 100644 --- a/hsv2rgb.cpp +++ b/hsv2rgb.cpp @@ -286,22 +286,22 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb) // Level Y2 is a strong boost. const uint8_t Y1 = 1; const uint8_t Y2 = 0; - + // G2: Whether to divide all greens by two. // Depends GREATLY on your particular LEDs const uint8_t G2 = 0; - + // Gscale: what to scale green down by. // Depends GREATLY on your particular LEDs const uint8_t Gscale = 0; - - + + uint8_t hue = hsv.hue; uint8_t sat = hsv.sat; uint8_t val = hsv.val; - + uint8_t offset = hue & 0x1F; // 0..31 - + // offset8 = offset * 8 uint8_t offset8 = offset; { @@ -319,11 +319,11 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb) offset8 <<= 3; #endif } - + uint8_t third = scale8( offset8, (256 / 3)); - + uint8_t r, g, b; - + if( ! (hue & 0x80) ) { // 0XX if( ! (hue & 0x40) ) { @@ -397,7 +397,7 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb) uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); g = K171 - twothirds; b = K85 + twothirds; - + } else { // 101 //case 5: // B -> P @@ -405,7 +405,7 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb) g = 0; FORCE_REFERENCE(g); b = K255 - third; - + } } else { if( ! (hue & 0x20) ) { @@ -415,7 +415,7 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb) g = 0; FORCE_REFERENCE(g); b = K171 - third; - + } else { // 111 //case 7: // K -> R @@ -423,38 +423,53 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb) g = 0; FORCE_REFERENCE(g); b = K85 - third; - + } } } - + // This is one of the good places to scale the green down, // although the client can scale green down as well. if( G2 ) g = g >> 1; if( Gscale ) g = scale8_video_LEAVING_R1_DIRTY( g, Gscale); - + // Scale down colors if we're desaturated at all // and add the brightness_floor to r, g, and b. if( sat != 255 ) { - - nscale8x3_video( r, g, b, sat); - - uint8_t desat = 255 - sat; - desat = scale8( desat, desat); - - uint8_t brightness_floor = desat; - r += brightness_floor; - g += brightness_floor; - b += brightness_floor; + if( sat == 0) { + r = 255; b = 255; g = 255; + } else { + //nscale8x3_video( r, g, b, sat); + if( r ) r = scale8_LEAVING_R1_DIRTY( r, sat) + 1; + if( g ) g = scale8_LEAVING_R1_DIRTY( g, sat) + 1; + if( b ) b = scale8_LEAVING_R1_DIRTY( b, sat) + 1; + cleanup_R1(); + + uint8_t desat = 255 - sat; + desat = scale8( desat, desat); + + uint8_t brightness_floor = desat; + r += brightness_floor; + g += brightness_floor; + b += brightness_floor; + } } - + // Now scale everything down if we're at value < 255. if( val != 255 ) { - + val = scale8_video_LEAVING_R1_DIRTY( val, val); - nscale8x3_video( r, g, b, val); + if( val == 0 ) { + r=0; g=0; b=0; + } else { + // nscale8x3_video( r, g, b, val); + if( r ) r = scale8_LEAVING_R1_DIRTY( r, val) + 1; + if( g ) g = scale8_LEAVING_R1_DIRTY( g, val) + 1; + if( b ) b = scale8_LEAVING_R1_DIRTY( b, val) + 1; + cleanup_R1(); + } } - + // Here we have the old AVR "missing std X+n" problem again // It turns out that fixing it winds up costing more than // not fixing it. @@ -498,41 +513,72 @@ CHSV rgb2hsv_approximate( const CRGB& rgb) uint8_t g = rgb.g; uint8_t b = rgb.b; uint8_t h, s, v; - + // find desaturation uint8_t desat = 255; if( r < desat) desat = r; if( g < desat) desat = g; if( b < desat) desat = b; - + // remove saturation from all channels r -= desat; g -= desat; b -= desat; - + + //Serial.print("desat="); Serial.print(desat); Serial.println(""); + + //uint8_t orig_desat = sqrt16( desat * 256); + //Serial.print("orig_desat="); Serial.print(orig_desat); Serial.println(""); + + // saturation is opposite of desaturation + s = 255 - desat; + //Serial.print("s.1="); Serial.print(s); Serial.println(""); + + if( s != 255 ) { + // undo 'dimming' of saturation + s = 255 - sqrt16( (255-s) * 256); + } + // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5 + // if( s != 255 ) s = (255 - (256.0 * sqrt( (float)(255-s) / 256.0))); + //Serial.print("s.2="); Serial.print(s); Serial.println(""); + + // at least one channel is now zero - // if all three channels are zero, we had a // shade of gray. - - uint16_t total = r + g + b; - - if( total == 0) { + if( (r + g + b) == 0) { // we pick hue zero for no special reason - return CHSV( 0, 0, desat); + return CHSV( 0, 0, 255 - s); } - - // since this wasn't a pure shade of gray, - // the interesting question is what hue is it - - // scale all channels up to a total of 255 - if( total != 255) { + + // scale all channels up to compensate for desaturation + if( s < 255) { + if( s == 0) s = 1; + uint32_t scaleup = 65535 / (s); + r = ((uint32_t)(r) * scaleup) / 256; + g = ((uint32_t)(g) * scaleup) / 256; + b = ((uint32_t)(b) * scaleup) / 256; + } + //Serial.print("r.2="); Serial.print(r); Serial.println(""); + //Serial.print("g.2="); Serial.print(g); Serial.println(""); + //Serial.print("b.2="); Serial.print(b); Serial.println(""); + + uint16_t total = r + g + b; + + //Serial.print("total="); Serial.print(total); Serial.println(""); + + // scale all channels up to compensate for low values + if( total < 255) { + if( total == 0) total = 1; uint32_t scaleup = 65535 / (total); r = ((uint32_t)(r) * scaleup) / 256; g = ((uint32_t)(g) * scaleup) / 256; b = ((uint32_t)(b) * scaleup) / 256; } - + //Serial.print("r.3="); Serial.print(r); Serial.println(""); + //Serial.print("g.3="); Serial.print(g); Serial.println(""); + //Serial.print("b.3="); Serial.print(b); Serial.println(""); + if( total > 255 ) { v = 255; } else { @@ -541,35 +587,44 @@ CHSV rgb2hsv_approximate( const CRGB& rgb) if( v != 255) v = sqrt16( v * 256); // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5 // if( v != 255) v = (256.0 * sqrt( (float)(v) / 256.0)); - + } - - // saturation is opposite of desaturation - s = 255 - desat; + + //Serial.print("v="); Serial.print(v); Serial.println(""); + + +#if 0 + + //#else if( v != 255) { // this part could probably use refinement/rethinking, // (but it doesn't overflow & wrap anymore) uint16_t s16; s16 = (s * 256); s16 /= v; + //Serial.print("s16="); Serial.print(s16); Serial.println(""); if( s16 < 256) { s = s16; } else { s = 255; // clamp to prevent overflow } } - - // undo 'dimming' of saturation - if( s != 255 ) s = 255 - sqrt16( (255-s) * 256); - // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5 - // if( s != 255 ) s = (255 - (256.0 * sqrt( (float)(255-s) / 256.0))); - +#endif + + //Serial.print("s.3="); Serial.print(s); Serial.println(""); + + + // since this wasn't a pure shade of gray, + // the interesting question is what hue is it + + + // start with which channel is highest // (ties don't matter) uint8_t highest = r; if( g > highest) highest = g; if( b > highest) highest = b; - + if( highest == r ) { // Red is highest. // Hue could be Purple/Pink-Red,Red-Orange,Orange-Yellow @@ -586,14 +641,22 @@ CHSV rgb2hsv_approximate( const CRGB& rgb) h = HUE_ORANGE; h += scale8( qsub8((g - 85) + (171 - r), 4), FIXFRAC8(32,85)); //221 } - + } else if ( highest == g) { // Green is highest // Hue could be Yellow-Green, Green-Aqua if( b == 0) { // if Blue is zero, we're in Yellow-Green + // G = 171..255 + // R = 171.. 0 h = HUE_YELLOW; - h += scale8( qadd8( qadd8((g - 128), (128 - r)), 4), FIXFRAC8(32,255)); // + uint8_t radj = scale8( qsub8(171,r), 47); //171..0 -> 0..171 -> 0..31 + uint8_t gadj = scale8( qsub8(g,171), 96); //171..255 -> 0..84 -> 0..31; + uint8_t rgadj = radj + gadj; + uint8_t hueadv = rgadj / 2; + h += hueadv; + //h += scale8( qadd8( 4, qadd8((g - 128), (128 - r))), + // FIXFRAC8(32,255)); // } else { // if Blue is nonzero we're in Green-Aqua if( (g-b) > b) { @@ -604,7 +667,7 @@ CHSV rgb2hsv_approximate( const CRGB& rgb) h += scale8( qsub8(b, 85), FIXFRAC8(8,42)); } } - + } else /* highest == b */ { // Blue is highest // Hue could be Aqua/Blue-Blue, Blue-Purple, Purple-Pink @@ -622,9 +685,17 @@ CHSV rgb2hsv_approximate( const CRGB& rgb) h += scale8( qsub8(r, 85), FIXFRAC8(32,85)); } } - + h += 1; return CHSV( h, s, v); } +// Examples that need work: +// 0,192,192 +// 192,64,64 +// 224,32,32 +// 252,0,126 +// 252,252,0 +// 252,252,126 + FASTLED_NAMESPACE_END @@ -1,6 +1,8 @@ #ifndef __INC_HSV2RGB_H #define __INC_HSV2RGB_H +#include "FastLED.h" + #include "pixeltypes.h" FASTLED_NAMESPACE_BEGIN diff --git a/led_sysdefs.h b/led_sysdefs.h index bb5fd8ff..c6cecd52 100644 --- a/led_sysdefs.h +++ b/led_sysdefs.h @@ -1,6 +1,8 @@ #ifndef __INC_LED_SYSDEFS_H #define __INC_LED_SYSDEFS_H +#include "FastLED.h" + #include "fastled_config.h" #if defined(NRF51) || defined(__RFduino__) @@ -1,10 +1,12 @@ +#ifndef __INC_LIB8TION_H +#define __INC_LIB8TION_H + +#include "FastLED.h" + #ifndef __INC_LED_SYSDEFS_H #error WTH? led_sysdefs needs to be included first #endif -#ifndef __INC_LIB8TION_H -#define __INC_LIB8TION_H - FASTLED_NAMESPACE_BEGIN /* @@ -211,6 +213,8 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun #define EASE8_C 1 #define AVG8_C 1 #define AVG7_C 1 +#define AVG16_C 1 +#define AVG15_C 1 #elif defined(__AVR__) @@ -225,6 +229,8 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun #define SUB8_C 0 #define AVG8_C 0 #define AVG7_C 0 +#define AVG16_C 0 +#define AVG15_C 0 #define QADD8_AVRASM 1 #define QADD7_AVRASM 1 @@ -234,6 +240,8 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun #define SUB8_AVRASM 1 #define AVG8_AVRASM 1 #define AVG7_AVRASM 1 +#define AVG16_AVRASM 1 +#define AVG15_AVRASM 1 // Note: these require hardware MUL instruction // -- sorry, ATtiny! @@ -285,6 +293,8 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun #define EASE8_C 1 #define AVG8_C 1 #define AVG7_C 1 +#define AVG16_C 1 +#define AVG15_C 1 #endif diff --git a/lib8tion/math8.h b/lib8tion/math8.h index 9e87adc4..f41352a9 100644 --- a/lib8tion/math8.h +++ b/lib8tion/math8.h @@ -163,6 +163,31 @@ LIB8STATIC uint8_t avg8( uint8_t i, uint8_t j) #endif } +/// Calculate an integer average of two unsigned +/// 16-bit integer values (uint16_t). +/// Fractional results are rounded down, e.g. avg16(20,41) = 30 +LIB8STATIC uint16_t avg16( uint16_t i, uint16_t j) +{ +#if AVG16_C == 1 + return (uint32_t)((uint32_t)(i) + (uint32_t)(j)) >> 1; +#elif AVG16_AVRASM == 1 + asm volatile( + /* First, add jLo (heh) to iLo, 9th bit overflows into C flag */ + "add %A[i], %A[j] \n\t" + /* Now, add C + jHi to iHi, 17th bit overflows into C flag */ + "adc %B[i], %B[j] \n\t" + /* Divide iHi by two, moving C flag into high 16th bit, old 9th bit now in C */ + "ror %B[i] \n\t" + /* Divide iLo by two, moving C flag into high 8th bit */ + "ror %A[i] \n\t" + : [i] "+a" (i) + : [j] "a" (j) ); + return i; +#else +#error "No implementation for avg16 available." +#endif +} + /// Calculate an integer average of two signed 7-bit /// integers (int8_t) @@ -185,6 +210,34 @@ LIB8STATIC int8_t avg7( int8_t i, int8_t j) #endif } +/// Calculate an integer average of two signed 15-bit +/// integers (int16_t) +/// If the first argument is even, result is rounded down. +/// If the first argument is odd, result is result up. +LIB8STATIC int16_t avg15( int16_t i, int16_t j) +{ +#if AVG15_C == 1 + return ((int32_t)((int32_t)(i) + (int32_t)(j)) >> 1) + (i & 0x1); +#elif AVG15_AVRASM == 1 + asm volatile( + /* first divide j by 2, throwing away lowest bit */ + "asr %B[j] \n\t" + "ror %A[j] \n\t" + /* now divide i by 2, with lowest bit going into C */ + "asr %B[i] \n\t" + "ror %A[i] \n\t" + /* add j + C to i */ + "adc %A[i], %A[j] \n\t" + "adc %B[i], %B[j] \n\t" + : [i] "+a" (i) + : [j] "a" (j) ); + return i; +#else +#error "No implementation for avg15 available." +#endif +} + + /// Calculate the remainder of one unsigned 8-bit /// value divided by anoter, aka A % M. /// Implemented by repeated subtraction, which is diff --git a/lib8tion/scale8.h b/lib8tion/scale8.h index 239e9dea..e6bdeefd 100644 --- a/lib8tion/scale8.h +++ b/lib8tion/scale8.h @@ -388,8 +388,7 @@ LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale ) result = ((uint32_t)(i) * (uint32_t)(scale)) / 65536; return result; #elif SCALE16_AVRASM == 1 - uint32_t result = 0; - const uint8_t zero = 0; + uint32_t result; asm volatile( // result.A-B = i.A x scale.A " mul %A[i], %A[scale] \n\t" @@ -406,12 +405,26 @@ LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale ) // well, in case we want to use this code for // a generic 16x16 multiply somewhere. + : [result] "=r" (result) + : [i] "r" (i), + [scale] "r" (scale) + : "r0", "r1" + ); + + asm volatile( // result.C-D = i.B x scale.B " mul %B[i], %B[scale] \n\t" //" mov %C[result], r0 \n\t" //" mov %D[result], r1 \n\t" " movw %C[result], r0 \n\t" + : [result] "+r" (result) + : [i] "r" (i), + [scale] "r" (scale) + : "r0", "r1" + ); + const uint8_t zero = 0; + asm volatile( // result.B-D += i.B x scale.A " mul %B[i], %A[scale] \n\t" @@ -435,6 +448,7 @@ LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale ) [zero] "r" (zero) : "r0", "r1" ); + result = result >> 16; return result; #else diff --git a/library.properties b/library.properties index f5093f33..db96f3c9 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=FastLED -version=3.1.0 +version=3.1.1 author=Daniel Garcia maintainer=Daniel Garcia <dgarcia@fastled.io> sentence=Multi-platform library for controlling dozens of different types of LEDs along with optimized math, effect, and noise functions. @@ -20,6 +20,37 @@ FL_PROGMEM static uint8_t const p[] = { 151,160,137,91,90,15, 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,151 }; + +#if FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW == 1 +#define AVG15(U,V) (((U)+(V)) >> 1) +#else +// See if we should use the inlined avg15 for AVR with MUL instruction +#if defined(__AVR__) && (LIB8_ATTINY == 0) +#define AVG15(U,V) (avg15_inline_avr_mul((U),(V))) +// inlined copy of avg15 for AVR with MUL instruction; cloned from math8.h +// Forcing this inline in the 3-D 16bit noise produces a 12% speedup overall, +// at a cost of just +8 bytes of net code size. +static int16_t inline __attribute__((always_inline)) avg15_inline_avr_mul( int16_t i, int16_t j) +{ + asm volatile( + /* first divide j by 2, throwing away lowest bit */ + "asr %B[j] \n\t" + "ror %A[j] \n\t" + /* now divide i by 2, with lowest bit going into C */ + "asr %B[i] \n\t" + "ror %A[i] \n\t" + /* add j + C to i */ + "adc %A[i], %A[j] \n\t" + "adc %B[i], %B[j] \n\t" + : [i] "+a" (i) + : [j] "a" (j) ); + return i; +} +#else +#define AVG15(U,V) (avg15((U),(V))) +#endif +#endif + // // #define FADE_12 #define FADE_16 @@ -58,7 +89,7 @@ static int16_t inline __attribute__((always_inline)) grad16(uint8_t hash, int16 if(hash&1) { u = -u; } if(hash&2) { v = -v; } - return (u+v)>>1; + return AVG15(u,v); #endif } @@ -69,7 +100,7 @@ static int16_t inline __attribute__((always_inline)) grad16(uint8_t hash, int16_ if(hash&1) { u = -u; } if(hash&2) { v = -v; } - return (u+v)>>1; + return AVG15(u,v); } static int16_t inline __attribute__((always_inline)) grad16(uint8_t hash, int16_t x) { @@ -81,7 +112,7 @@ static int16_t inline __attribute__((always_inline)) grad16(uint8_t hash, int16_ if(hash&1) { u = -u; } if(hash&2) { v = -v; } - return (u+v)>>1; + return AVG15(u,v); } // selectBasedOnHashBit performs this: @@ -290,7 +321,13 @@ uint16_t inoise16(uint32_t x, uint32_t y, uint32_t z) { int32_t ans = inoise16_raw(x,y,z); ans = ans + 19052L; uint32_t pan = ans; - return (pan*220L)>>7; + // pan = (ans * 220L) >> 7. That's the same as: + // pan = (ans * 440L) >> 8. And this way avoids a 7X four-byte shift-loop on AVR. + // Identical math, except for the highest bit, which we don't care about anyway, + // since we're returning the 'middle' 16 out of a 32-bit value anyway. + pan *= 440L; + return (pan>>8); + // // return scale16by8(pan,220)<<1; // return ((inoise16_raw(x,y,z)+19052)*220)>>7; // return scale16by8(inoise16_raw(x,y,z)+19052,220)<<1; @@ -333,7 +370,13 @@ uint16_t inoise16(uint32_t x, uint32_t y) { int32_t ans = inoise16_raw(x,y); ans = ans + 17308L; uint32_t pan = ans; - return (pan*242L)>>7; + // pan = (ans * 242L) >> 7. That's the same as: + // pan = (ans * 484L) >> 8. And this way avoids a 7X four-byte shift-loop on AVR. + // Identical math, except for the highest bit, which we don't care about anyway, + // since we're returning the 'middle' 16 out of a 32-bit value anyway. + pan *= 484L; + return (pan>>8); + // return (uint32_t)(((int32_t)inoise16_raw(x,y)+(uint32_t)17308)*242)>>7; // return scale16by8(inoise16_raw(x,y)+17308,242)<<1; } @@ -655,7 +698,7 @@ void fill_noise8(CRGB *leds, int num_leds, void fill_noise16(CRGB *leds, int num_leds, uint8_t octaves, uint16_t x, int scale, uint8_t hue_octaves, uint16_t hue_x, int hue_scale, - uint16_t time) { + uint16_t time, uint8_t hue_shift) { uint8_t V[num_leds]; uint8_t H[num_leds]; @@ -666,7 +709,7 @@ void fill_noise16(CRGB *leds, int num_leds, fill_raw_noise8(H,num_leds,hue_octaves,hue_x,hue_scale,time); for(int i = 0; i < num_leds; i++) { - leds[i] = CHSV(H[i],255,V[i]); + leds[i] = CHSV(H[i] + hue_shift,255,V[i]); } } @@ -1,6 +1,8 @@ #ifndef __INC_NOISE_H #define __INC_NOISE_H +#include "FastLED.h" + FASTLED_NAMESPACE_BEGIN ///@file noise.h @@ -81,7 +83,7 @@ void fill_noise8(CRGB *leds, int num_leds, void fill_noise16(CRGB *leds, int num_leds, uint8_t octaves, uint16_t x, int scale, uint8_t hue_octaves, uint16_t hue_x, int hue_scale, - uint16_t time); + uint16_t time, uint8_t hue_shift=0); void fill_2dnoise8(CRGB *leds, int width, int height, bool serpentine, uint8_t octaves, uint16_t x, int xscale, uint16_t y, int yscale, uint16_t time, uint8_t hue_octaves, uint16_t hue_x, int hue_xscale, uint16_t hue_y, uint16_t hue_yscale,uint16_t hue_time,bool blend); diff --git a/pixelset.h b/pixelset.h new file mode 100644 index 00000000..345b6071 --- /dev/null +++ b/pixelset.h @@ -0,0 +1,300 @@ +#ifndef __INC_PIXELSET_H +#define __INC_PIXELSET_H + +#include "FastLED.h" + +/// Represents a set of CRGB led objects. Provides the [] array operator, and works like a normal array in that case. +/// This should be kept in sync with the set of functions provided by CRGB as well as functions in colorutils. Note +/// that a pixel set is a window into another set of led data, it is not its own set of led data. +template<class PIXEL_TYPE> +class CPixelView { +public: + const int8_t dir; + const int len; + PIXEL_TYPE * const leds; + PIXEL_TYPE * const end_pos; + +public: + + /// PixelSet copy constructor + inline CPixelView(const CPixelView & other) : dir(other.dir), len(other.len), leds(other.leds), end_pos(other.end_pos) {} + + /// pixelset constructor for a pixel set starting at the given PIXEL_TYPE* and going for _len leds. Note that the length + /// can be backwards, creating a PixelSet that walks backwards over the data + /// @param leds point to the raw led data + /// @param len how many leds in this set + inline CPixelView(PIXEL_TYPE *_leds, int _len) : dir(_len < 0 ? -1 : 1), len(_len), leds(_leds), end_pos(_leds + _len) {} + + /// PixelSet constructor for the given set of leds, with start and end boundaries. Note that start can be after + /// end, resulting in a set that will iterate backwards + /// @param leds point to the raw led data + /// @param start the start index of the leds for this array + /// @param end the end index of the leds for this array + inline CPixelView(PIXEL_TYPE *_leds, int _start, int _end) : dir(((_end-_start)<0) ? -1 : 1), len((_end - _start) + dir), leds(_leds + _start), end_pos(_leds + _start + len) {} + + /// Get the size of this set + /// @return the size of the set + int size() { return abs(len); } + + /// Whether or not this set goes backwards + /// @return whether or not the set is backwards + bool reversed() { return len < 0; } + + /// do these sets point to the same thing (note, this is different from the contents of the set being the same) + bool operator==(const CPixelView & rhs) const { return leds == rhs.leds && len == rhs.len && dir == rhs.dir; } + + /// do these sets point to the different things (note, this is different from the contents of the set being the same) + bool operator!=(const CPixelView & rhs) const { return leds != rhs.leds || len != rhs.len || dir != rhs.dir; } + + /// access a single element in this set, just like an array operator + inline PIXEL_TYPE & operator[](int x) const { if(dir & 0x80) { return leds[-x]; } else { return leds[x]; } } + + /// Access an inclusive subset of the leds in this set. Note that start can be greater than end, which will + /// result in a reverse ordering for many functions (useful for mirroring) + /// @param start the first element from this set for the new subset + /// @param end the last element for the new subset + inline CPixelView operator()(int start, int end) { return CPixelView(leds, start, end); } + + /// Access an inclusive subset of the leds in this set, starting from the first. + /// @param end the last element for the new subset + /// Not sure i want this? inline CPixelView operator()(int end) { return CPixelView(leds, 0, end); } + + /// Return the reverse ordering of this set + inline CPixelView operator-() { return CPixelView(leds + len - dir, len - dir, 0); } + + /// Return a pointer to the first element in this set + inline operator PIXEL_TYPE* () const { return leds; } + + /// Assign the passed in color to all elements in this set + /// @param color the new color for the elements in the set + inline CPixelView & operator=(const PIXEL_TYPE & color) { + for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) = color; } + return *this; + } + + + void dump() const { +/** + Serial.print("len: "); Serial.print(len); Serial.print(", dir:"); Serial.print((int)dir); + Serial.print(", range:"); Serial.print((uint32_t)leds); Serial.print("-"); Serial.print((uint32_t)end_pos); + Serial.print(", diff:"); Serial.print((int32_t)(end_pos - leds)); + Serial.println(""); + **/ + } + + /// Copy the contents of the passed in set to our set. Note if one set is smaller than the other, only the + /// smallest number of items will be copied over. + inline CPixelView & operator=(const CPixelView & rhs) { + for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { + (*pixel) = (*rhspixel); + } + return *this; + } + + /// @name modification/scaling operators + //@{ + /// Add the passed in value to r,g, b for all the pixels in this set + inline CPixelView & addToRGB(uint8_t inc) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) += inc; } return *this; } + /// Add every pixel in the other set to this set + inline CPixelView & operator+=(CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) += (*rhspixel); } return *this; } + + /// Subtract the passed in value from r,g,b for all pixels in this set + inline CPixelView & subFromRGB(uint8_t inc) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) -= inc; } return *this; } + /// Subtract every pixel in the other set from this set + inline CPixelView & operator-=(CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) -= (*rhspixel); } return *this; } + + /// Increment every pixel value in this set + inline CPixelView & operator++() { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)++; } return *this; } + /// Increment every pixel value in this set + inline CPixelView & operator++(int DUMMY_ARG) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)++; } return *this; } + + /// Decrement every pixel value in this set + inline CPixelView & operator--() { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)--; } return *this; } + /// Decrement every pixel value in this set + inline CPixelView & operator--(int DUMMY_ARG) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)--; } return *this; } + + /// Divide every led by the given value + inline CPixelView & operator/=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) /= d; } return *this; } + /// Shift every led in this set right by the given number of bits + inline CPixelView & operator>>=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) >>= d; } return *this; } + /// Multiply every led in this set by the given value + inline CPixelView & operator*=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) *= d; } return *this; } + + /// Scale every led by the given scale + inline CPixelView & nscale8_video(uint8_t scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8_video(scaledown); } return *this;} + /// Scale down every led by the given scale + inline CPixelView & operator%=(uint8_t scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8_video(scaledown); } return *this; } + /// Fade every led down by the given scale + inline CPixelView & fadeLightBy(uint8_t fadefactor) { return nscale8_video(255 - fadefactor); } + + /// Scale every led by the given scale + inline CPixelView & nscale8(uint8_t scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8(scaledown); } return *this; } + /// Scale every led by the given scale + inline CPixelView & nscale8(PIXEL_TYPE & scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8(scaledown); } return *this; } + /// Scale every led in this set by every led in the other set + inline CPixelView & nscale8(CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel).nscale8((*rhspixel)); } return *this; } + + /// Fade every led down by the given scale + inline CPixelView & fadeToBlackBy(uint8_t fade) { return nscale8(255 - fade); } + + /// Apply the PIXEL_TYPE |= operator to every pixel in this set with the given PIXEL_TYPE value (bringing each channel to the higher of the two values) + inline CPixelView & operator|=(const PIXEL_TYPE & rhs) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) |= rhs; } return *this; } + /// Apply the PIXEL_TYPE |= operator to every pixel in this set with every pixel in the passed in set + inline CPixelView & operator|=(const CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) |= (*rhspixel); } return *this; } + /// Apply the PIXEL_TYPE |= operator to every pixel in this set + inline CPixelView & operator|=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) |= d; } return *this; } + + /// Apply the PIXEL_TYPE &= operator to every pixel in this set with the given PIXEL_TYPE value (bringing each channel down to the lower of the two values) + inline CPixelView & operator&=(const PIXEL_TYPE & rhs) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) &= rhs; } return *this; } + /// Apply the PIXEL_TYPE &= operator to every pixel in this set with every pixel in the passed in set + inline CPixelView & operator&=(const CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) &= (*rhspixel); } return *this; } + /// APply the PIXEL_TYPE &= operator to every pixel in this set with the passed in value + inline CPixelView & operator&=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) &= d; } return *this; } + //@} + + /// Returns whether or not any leds in this set are non-zero + inline operator bool() { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { if((*pixel)) return true; } return false; } + + // Color util functions + inline CPixelView & fill_solid(const PIXEL_TYPE & color) { *this = color; return *this; } + inline CPixelView & fill_solid(const CHSV & color) { if(dir>0) { *this = color; return *this; } } + + inline CPixelView & fill_rainbow(uint8_t initialhue, uint8_t deltahue=5) { + if(dir >= 0) { + ::fill_rainbow(leds,len,initialhue,deltahue); + } else { + ::fill_rainbow(leds+len+1,-len,initialhue,deltahue); + } + return *this; + } + + inline CPixelView & fill_gradient(const CHSV & startcolor, const CHSV & endcolor, TGradientDirectionCode directionCode = SHORTEST_HUES) { + if(dir >= 0) { + ::fill_gradient(leds,len,startcolor, endcolor, directionCode); + } else { + ::fill_gradient(leds + len + 1, (-len), endcolor, startcolor, directionCode); + } + return *this; + } + + inline CPixelView & fill_gradient(const CHSV & c1, const CHSV & c2, const CHSV & c3, TGradientDirectionCode directionCode = SHORTEST_HUES) { + if(dir >= 0) { + ::fill_gradient(leds, len, c1, c2, c3, directionCode); + } else { + ::fill_gradient(leds + len + 1, -len, c3, c2, c1, directionCode); + } + return *this; + } + + inline CPixelView & fill_gradient(const CHSV & c1, const CHSV & c2, const CHSV & c3, const CHSV & c4, TGradientDirectionCode directionCode = SHORTEST_HUES) { + if(dir >= 0) { + ::fill_gradient(leds, len, c1, c2, c3, c4, directionCode); + } else { + ::fill_gradient(leds + len + 1, -len, c4, c3, c2, c1, directionCode); + } + return *this; + } + + inline CPixelView & fill_gradient_RGB(const PIXEL_TYPE & startcolor, const PIXEL_TYPE & endcolor, TGradientDirectionCode directionCode = SHORTEST_HUES) { + if(dir >= 0) { + ::fill_gradient_RGB(leds,len,startcolor, endcolor); + } else { + ::fill_gradient_RGB(leds + len + 1, (-len), endcolor, startcolor); + } + return *this; + } + + inline CPixelView & fill_gradient_RGB(const PIXEL_TYPE & c1, const PIXEL_TYPE & c2, const PIXEL_TYPE & c3) { + if(dir >= 0) { + ::fill_gradient_RGB(leds, len, c1, c2, c3); + } else { + ::fill_gradient_RGB(leds + len + 1, -len, c3, c2, c1); + } + return *this; + } + + inline CPixelView & fill_gradient_RGB(const PIXEL_TYPE & c1, const PIXEL_TYPE & c2, const PIXEL_TYPE & c3, const PIXEL_TYPE & c4) { + if(dir >= 0) { + ::fill_gradient_RGB(leds, len, c1, c2, c3, c4); + } else { + ::fill_gradient_RGB(leds + len + 1, -len, c4, c3, c2, c1); + } + return *this; + } + + inline CPixelView & nblend(const PIXEL_TYPE & overlay, fract8 amountOfOverlay) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { ::nblend((*pixel), overlay, amountOfOverlay); } return *this; } + inline CPixelView & nblend(const CPixelView & rhs, fract8 amountOfOverlay) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { ::nblend((*pixel), (*rhspixel), amountOfOverlay); } return *this; } + + // Note: only bringing in a 1d blur, not sure 2d blur makes sense when looking at sub arrays + inline CPixelView & blur1d(fract8 blur_amount) { + if(dir >= 0) { + ::blur1d(leds, len, blur_amount); + } else { + ::blur1d(leds + len + 1, -len, blur_amount); + } + return *this; + } + + inline CPixelView & napplyGamma_video(float gamma) { + if(dir >= 0) { + ::napplyGamma_video(leds, len, gamma); + } else { + ::napplyGamma_video(leds + len + 1, -len, gamma); + } + return *this; + } + + inline CPixelView & napplyGamma_video(float gammaR, float gammaG, float gammaB) { + if(dir >= 0) { + ::napplyGamma_video(leds, len, gammaR, gammaG, gammaB); + } else { + ::napplyGamma_video(leds + len + 1, -len, gammaR, gammaG, gammaB); + } + return *this; + } + + // TODO: Make this a fully specified/proper iterator + template <class T> + class pixelset_iterator_base { + T * leds; + const int8_t dir; + public: + __attribute__((always_inline)) inline pixelset_iterator_base(const pixelset_iterator_base & rhs) : leds(rhs.leds), dir(rhs.dir) {} + __attribute__((always_inline)) inline pixelset_iterator_base(T * _leds, const char _dir) : leds(_leds), dir(_dir) {} + + __attribute__((always_inline)) inline pixelset_iterator_base& operator++() { leds += dir; return *this; } + __attribute__((always_inline)) inline pixelset_iterator_base operator++(int) { pixelset_iterator_base tmp(*this); leds += dir; return tmp; } + + __attribute__((always_inline)) inline bool operator==(pixelset_iterator_base & other) const { return leds == other.leds; } // && set==other.set; } + __attribute__((always_inline)) inline bool operator!=(pixelset_iterator_base & other) const { return leds != other.leds; } // || set != other.set; } + + __attribute__((always_inline)) inline PIXEL_TYPE& operator*() const { return *leds; } + }; + + typedef pixelset_iterator_base<PIXEL_TYPE> iterator; + typedef pixelset_iterator_base<const PIXEL_TYPE> const_iterator; + + iterator begin() { return iterator(leds, dir); } + iterator end() { return iterator(end_pos, dir); } + + iterator begin() const { return iterator(leds, dir); } + iterator end() const { return iterator(end_pos, dir); } + + const_iterator cbegin() const { return const_iterator(leds, dir); } + const_iterator cend() const { return const_iterator(end_pos, dir); } +}; + +typedef CPixelView<CRGB> CRGBSet; + +__attribute__((always_inline)) +inline CRGB *operator+(const CRGBSet & pixels, int offset) { return (CRGB*)pixels + offset; } + + +template<int SIZE> +class CRGBArray : public CPixelView<CRGB> { + CRGB rawleds[SIZE]; +public: + CRGBArray() : CPixelView<CRGB>(rawleds, SIZE) {} +}; + +#endif diff --git a/pixeltypes.h b/pixeltypes.h index 8ff9120b..89225ae1 100644 --- a/pixeltypes.h +++ b/pixeltypes.h @@ -1,6 +1,8 @@ #ifndef __INC_PIXELS_H #define __INC_PIXELS_H +#include "FastLED.h" + #include <stdint.h> #include "lib8tion.h" #include "color.h" diff --git a/platforms.h b/platforms.h index 4f5d7ee6..c36b016c 100644 --- a/platforms.h +++ b/platforms.h @@ -1,6 +1,8 @@ #ifndef __INC_PLATFORMS_H #define __INC_PLATFORMS_H +#include "FastLED.h" + #include "fastled_config.h" #if defined(NRF51) diff --git a/platforms/avr/fastpin_avr.h b/platforms/avr/fastpin_avr.h index a4cff31c..69a01a14 100644 --- a/platforms/avr/fastpin_avr.h +++ b/platforms/avr/fastpin_avr.h @@ -30,9 +30,9 @@ public: inline static void toggle() __attribute__ ((always_inline)) { _PIN::r() = _MASK; } - inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } - inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } - inline static void fastset(register port_ptr_t port, register uint8_t val) __attribute__ ((always_inline)) { set(val); } + inline static void hi(register port_ptr_t /*port*/) __attribute__ ((always_inline)) { hi(); } + inline static void lo(register port_ptr_t /*port*/) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(register port_ptr_t /*port*/, register uint8_t val) __attribute__ ((always_inline)) { set(val); } inline static port_t hival() __attribute__ ((always_inline)) { return _PORT::r() | _MASK; } inline static port_t loval() __attribute__ ((always_inline)) { return _PORT::r() & ~_MASK; } @@ -141,25 +141,50 @@ _DEFPIN_AVR(16, 0x04, C); _DEFPIN_AVR(17, 0x08, C); _DEFPIN_AVR(18, 0x10, C); _D #define AVR_HARDWARE_SPI 1 #define HAS_HARDWARE_PIN_SUPPORT 1 +#ifndef __AVR_ATmega8__ #define SPI_UART0_DATA 1 #define SPI_UART0_CLOCK 4 +#endif #elif defined(__AVR_ATmega1284P__) _IO(A); _IO(B); _IO(C); _IO(D); -_DEFPIN_AVR(0, 1<<0, D); _DEFPIN_AVR(1, 1<<1, D); _DEFPIN_AVR(2, 1<<2, B); _DEFPIN_AVR(3, 1<<3, B); -_DEFPIN_AVR(4, 1<<0, B); _DEFPIN_AVR(5, 1<<1, B); _DEFPIN_AVR(6, 1<<2, D); _DEFPIN_AVR(7, 1<<3, D); -_DEFPIN_AVR(8, 1<<5, D); _DEFPIN_AVR(9, 1<<6, D); _DEFPIN_AVR(10, 1<<4, B); _DEFPIN_AVR(11, 1<<5, B); -_DEFPIN_AVR(12, 1<<6, B); _DEFPIN_AVR(13, 1<<7, B); _DEFPIN_AVR(14, 1<<7, A); _DEFPIN_AVR(15, 1<<6, A); -_DEFPIN_AVR(16, 1<<5, A); _DEFPIN_AVR(17, 1<<4, A); _DEFPIN_AVR(18, 1<<3, A); _DEFPIN_AVR(19, 1<<2, A); -_DEFPIN_AVR(20, 1<<1, A); _DEFPIN_AVR(21, 1<<0, A); _DEFPIN_AVR(22, 1<<0, C); _DEFPIN_AVR(23, 1<<1, C); -_DEFPIN_AVR(24, 1<<2, C); _DEFPIN_AVR(25, 1<<3, C); _DEFPIN_AVR(26, 1<<4, C); _DEFPIN_AVR(27, 1<<5, C); -_DEFPIN_AVR(28, 1<<6, C); _DEFPIN_AVR(29, 1<<7, C); _DEFPIN_AVR(30, 1<<4, D); _DEFPIN_AVR(31, 1<<7, D); +#define MAX_PIN 31 +_DEFPIN_AVR(0, 1<<0, B); _DEFPIN_AVR(1, 1<<1, B); _DEFPIN_AVR(2, 1<<2, B); _DEFPIN_AVR(3, 1<<3, B); +_DEFPIN_AVR(4, 1<<4, B); _DEFPIN_AVR(5, 1<<5, B); _DEFPIN_AVR(6, 1<<6, B); _DEFPIN_AVR(7, 1<<7, B); +_DEFPIN_AVR(8, 1<<0, D); _DEFPIN_AVR(9, 1<<1, D); _DEFPIN_AVR(10, 1<<2, D); _DEFPIN_AVR(11, 1<<3, D); +_DEFPIN_AVR(12, 1<<4, D); _DEFPIN_AVR(13, 1<<5, D); _DEFPIN_AVR(14, 1<<6, D); _DEFPIN_AVR(15, 1<<7, D); +_DEFPIN_AVR(16, 1<<0, C); _DEFPIN_AVR(17, 1<<1, C); _DEFPIN_AVR(18, 1<<2, C); _DEFPIN_AVR(19, 1<<3, C); +_DEFPIN_AVR(20, 1<<4, C); _DEFPIN_AVR(21, 1<<5, C); _DEFPIN_AVR(22, 1<<6, C); _DEFPIN_AVR(23, 1<<7, C); +_DEFPIN_AVR(24, 1<<0, A); _DEFPIN_AVR(25, 1<<1, A); _DEFPIN_AVR(26, 1<<2, A); _DEFPIN_AVR(27, 1<<3, A); +_DEFPIN_AVR(28, 1<<4, A); _DEFPIN_AVR(29, 1<<5, A); _DEFPIN_AVR(30, 1<<6, A); _DEFPIN_AVR(31, 1<<7, A); + +#define SPI_DATA 5 +#define SPI_CLOCK 7 +#define SPI_SELECT 4 +#define AVR_HARDWARE_SPI 1 +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(__AVR_ATmega128RFA1__) || defined(__AVR_ATmega256RFR2__) + +// AKA the Pinoccio + +_IO(A); _IO(B); _IO(C); _IO(D); _IO(E); _IO(F); + +_DEFPIN_AVR( 0, 1<<0, E); _DEFPIN_AVR( 1, 1<<1, E); _DEFPIN_AVR( 2, 1<<7, B); _DEFPIN_AVR( 3, 1<<3, E); +_DEFPIN_AVR( 4, 1<<4, E); _DEFPIN_AVR( 5, 1<<5, E); _DEFPIN_AVR( 6, 1<<2, E); _DEFPIN_AVR( 7, 1<<6, E); +_DEFPIN_AVR( 8, 1<<5, D); _DEFPIN_AVR( 9, 1<<0, B); _DEFPIN_AVR(10, 1<<2, B); _DEFPIN_AVR(11, 1<<3, B); +_DEFPIN_AVR(12, 1<<1, B); _DEFPIN_AVR(13, 1<<2, D); _DEFPIN_AVR(14, 1<<3, D); _DEFPIN_AVR(15, 1<<0, D); +_DEFPIN_AVR(16, 1<<1, D); _DEFPIN_AVR(17, 1<<4, D); _DEFPIN_AVR(18, 1<<7, E); _DEFPIN_AVR(19, 1<<6, D); +_DEFPIN_AVR(20, 1<<7, D); _DEFPIN_AVR(21, 1<<4, B); _DEFPIN_AVR(22, 1<<5, B); _DEFPIN_AVR(23, 1<<6, B); +_DEFPIN_AVR(24, 1<<0, F); _DEFPIN_AVR(25, 1<<1, F); _DEFPIN_AVR(26, 1<<2, F); _DEFPIN_AVR(27, 1<<3, F); +_DEFPIN_AVR(28, 1<<4, F); _DEFPIN_AVR(29, 1<<5, F); _DEFPIN_AVR(30, 1<<6, F); _DEFPIN_AVR(31, 1<<7, F); + +#define SPI_DATA 10 +#define SPI_CLOCK 12 +#define SPI_SELECT 9 -#define SPI_DATA 11 -#define SPI_CLOCK 13 -#define SPI_SELECT 10 #define AVR_HARDWARE_SPI 1 #define HAS_HARDWARE_PIN_SUPPORT 1 @@ -253,14 +278,15 @@ _DEFPIN_AVR(44, 64, F); _DEFPIN_AVR(45, 128, F); // leonard defs _IO(B); _IO(C); _IO(D); _IO(E); _IO(F); -#define MAX_PIN 24 +#define MAX_PIN 30 _DEFPIN_AVR(0, 4, D); _DEFPIN_AVR(1, 8, D); _DEFPIN_AVR(2, 2, D); _DEFPIN_AVR(3, 1, D); _DEFPIN_AVR(4, 16, D); _DEFPIN_AVR(5, 64, C); _DEFPIN_AVR(6, 128, D); _DEFPIN_AVR(7, 64, E); _DEFPIN_AVR(8, 16, B); _DEFPIN_AVR(9, 32, B); _DEFPIN_AVR(10, 64, B); _DEFPIN_AVR(11, 128, B); _DEFPIN_AVR(12, 64, D); _DEFPIN_AVR(13, 128, C); _DEFPIN_AVR(14, 8, B); _DEFPIN_AVR(15, 2, B); _DEFPIN_AVR(16, 4, B); _DEFPIN_AVR(17, 1, B); _DEFPIN_AVR(18, 128, F); _DEFPIN_AVR(19, 64, F); _DEFPIN_AVR(20, 32, F); _DEFPIN_AVR(21, 16, F); _DEFPIN_AVR(22, 2, F); _DEFPIN_AVR(23, 1, F); - _DEFPIN_AVR(24, 32, D); +_DEFPIN_AVR(24, 16, D); _DEFPIN_AVR(25, 128, D); _DEFPIN_AVR(26, 16, B); _DEFPIN_AVR(27, 32, B); +_DEFPIN_AVR(28, 64, B); _DEFPIN_AVR(29, 64, D); _DEFPIN_AVR(30, 32, D); #define SPI_DATA 16 #define SPI_CLOCK 15 @@ -269,7 +295,7 @@ _DEFPIN_AVR(20, 32, F); _DEFPIN_AVR(21, 16, F); _DEFPIN_AVR(22, 2, F); _DEFPIN_A // PD3/PD5 #define SPI_UART1_DATA 1 -#define SPI_UART1_CLOCK 24 +#define SPI_UART1_CLOCK 30 #endif diff --git a/power_mgt.h b/power_mgt.h index 9ff6f208..5d0ec61c 100644 --- a/power_mgt.h +++ b/power_mgt.h @@ -1,6 +1,8 @@ #ifndef POWER_MGT_H #define POWER_MGT_H +#include "FastLED.h" + #include "pixeltypes.h" FASTLED_NAMESPACE_BEGIN diff --git a/release_notes.md b/release_notes.md index 2258ab46..e59d1de4 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,4 +1,9 @@ -FastLED3.1.1pre +FastLED3.1.2pre +=============== + +* Add SK6822 timings + +FastLED3.1.1 ============ * Enabled RFDuino/nrf51822 hardware SPI support * Fix edge case bug w/HSV palette blending @@ -7,6 +12,10 @@ FastLED3.1.1pre * Roll power management into FastLED.show/delay directly * Support for adafruit pixies on arduino type platforms that have SoftwareSerial * TODO: support hardware serial on platforms that have it available +* Add UCS2903 timings +* Preliminary CPixelView/CRGBSet code - more flexible treatment of groups of arrays + * https://github.com/FastLED/FastLED/wiki/RGBSet-Reference + FastLED3.1.0 ============ |