diff --git a/FastLED.h b/FastLED.h index 6e2e03d6bc..eb078b6462 100644 --- a/FastLED.h +++ b/FastLED.h @@ -104,7 +104,8 @@ template class UCS2903 : public UCS2903Contr template class WS2812 : public WS2812Controller800Khz {}; template class WS2852 : public WS2812Controller800Khz {}; template class WS2812B : public WS2812Controller800Khz {}; -template class SK6812 : public SK6812Controller {}; +template class SK6812 : public SK6812Controller {}; +template class SK6812_RGBW : public SK6812Controller {}; template class SK6822 : public SK6822Controller {}; template class APA106 : public SK6822Controller {}; template class PL9823 : public PL9823Controller {}; @@ -131,6 +132,7 @@ enum EBlockChipsets { WS2811_400_PORTA, TM1803_PORTA, UCS1903_PORTA, + SK6812_PORTA, #endif #ifdef PORTB_FIRST_PIN WS2811_PORTB, @@ -424,6 +426,7 @@ class CFastLED { case WS2813_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); case TM1803_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); case UCS1903_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case SK6812_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); #endif #ifdef PORTB_FIRST_PIN case WS2811_PORTB: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); diff --git a/chipsets.h b/chipsets.h index c41dbb986f..6cf830f6c7 100644 --- a/chipsets.h +++ b/chipsets.h @@ -491,8 +491,8 @@ class LPD1886Controller1250Khz_8bit : public ClocklessController class SK6822Controller : public ClocklessController {}; -template -class SK6812Controller : public ClocklessController {}; +template +class SK6812Controller : public ClocklessController {}; template class PL9823Controller : public ClocklessController {}; diff --git a/controller.h b/controller.h index cd225337d8..1130274b6b 100644 --- a/controller.h +++ b/controller.h @@ -12,8 +12,9 @@ FASTLED_NAMESPACE_BEGIN -#define RO(X) RGB_BYTE(RGB_ORDER, X) -#define RGB_BYTE(RO,X) (((RO)>>(3*(2-(X)))) & 0x3) +// special case X==3, which is gonna be white in our hacky new system.... +#define RO(X) ( ( X < 3 ) ? RGB_BYTE(RGB_ORDER, X) : 3) +#define RGB_BYTE(RO, X) (((RO)>>(3*(2-(X)))) & 0x3) #define RGB_BYTE0(RO) ((RO>>6) & 0x3) #define RGB_BYTE1(RO) ((RO>>3) & 0x3) @@ -23,6 +24,7 @@ FASTLED_NAMESPACE_BEGIN #define DISABLE_DITHER 0x00 #define BINARY_DITHER 0x01 + typedef uint8_t EDitherMode; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -39,6 +41,7 @@ typedef uint8_t EDitherMode; class CLEDController { protected: friend class CFastLED; + CRGB *m_Data; CLEDController *m_pNext; CRGB m_ColorCorrection; @@ -52,12 +55,12 @@ class CLEDController { ///@param data the crgb color to set the leds to ///@param nLeds the numner of leds to set to this color ///@param scale the rgb scaling value for outputting color - virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) = 0; + virtual void showColor(const struct CRGB &data, int nLeds, CRGB scale) = 0; - /// write the passed in rgb data out to the leds managed by this controller - ///@param data the rgb data to write out to the strip - ///@param nLeds the number of leds being written out - ///@param scale the rgb scaling to apply to each led before writing it out + /// write the passed in rgb data out to the leds managed by this controller + ///@param data the rgb data to write out to the strip + ///@param nLeds the number of leds being written out + ///@param scale the rgb scaling to apply to each led before writing it out virtual void show(const struct CRGB *data, int nLeds, CRGB scale) = 0; /// Once we know the number of LED's, we can init any underlying buffers @@ -65,19 +68,20 @@ class CLEDController { virtual void initLedBuffers() {} public: - /// create an led controller object, add it to the chain of controllers - CLEDController() : m_Data(NULL), m_ColorCorrection(UncorrectedColor), m_ColorTemperature(UncorrectedTemperature), m_DitherMode(BINARY_DITHER), m_nLeds(0) { + /// create an led controller object, add it to the chain of controllers + CLEDController() : m_Data(NULL), m_ColorCorrection(UncorrectedColor), m_ColorTemperature(UncorrectedTemperature), + m_DitherMode(BINARY_DITHER), m_nLeds(0) { m_pNext = NULL; - if(m_pHead==NULL) { m_pHead = this; } - if(m_pTail != NULL) { m_pTail->m_pNext = this; } + if (m_pHead == NULL) { m_pHead = this; } + if (m_pTail != NULL) { m_pTail->m_pNext = this; } m_pTail = this; } - ///initialize the LED controller - virtual void init() = 0; + ///initialize the LED controller + virtual void init() = 0; - ///clear out/zero out the given number of leds. - virtual void clearLeds(int nLeds) { showColor(CRGB::Black, nLeds, CRGB::Black); } + ///clear out/zero out the given number of leds. + virtual void clearLeds(int nLeds) { showColor(CRGB::Black, nLeds, CRGB::Black); } /// show function w/integer brightness, will scale for color correction and temperature void show(const struct CRGB *data, int nLeds, uint8_t brightness) { @@ -90,32 +94,33 @@ class CLEDController { } /// show function using the "attached to this controller" led data - void showLeds(uint8_t brightness=255) { + void showLeds(uint8_t brightness = 255) { show(m_Data, m_nLeds, getAdjustment(brightness)); } - /// show the given color on the led strip - void showColor(const struct CRGB & data, uint8_t brightness=255) { + /// show the given color on the led strip + void showColor(const struct CRGB &data, uint8_t brightness = 255) { showColor(data, m_nLeds, getAdjustment(brightness)); } /// get the first led controller in the chain of controllers static CLEDController *head() { return m_pHead; } + /// get the next controller in the chain after this one. will return NULL at the end of the chain CLEDController *next() { return m_pNext; } - /// set the default array of leds to be used by this controller - CLEDController & setLeds(CRGB *data, int nLeds) { + /// set the default array of leds to be used by this controller + CLEDController &setLeds(CRGB *data, int nLeds) { m_Data = data; m_nLeds = nLeds; initLedBuffers(); return *this; } - /// zero out the led data managed by this controller + /// zero out the led data managed by this controller void clearLedData() { - if(m_Data) { - memset8((void*)m_Data, 0, sizeof(struct CRGB) * m_nLeds); + if (m_Data) { + memset8((void *) m_Data, 0, sizeof(struct CRGB) * m_nLeds); } } @@ -123,129 +128,171 @@ class CLEDController { virtual int size() { return m_nLeds; } /// Pointer to the CRGB array for this controller - CRGB* leds() { return m_Data; } + CRGB *leds() { return m_Data; } /// Reference to the n'th item in the controller CRGB &operator[](int x) { return m_Data[x]; } - /// set the dithering mode for this controller to use - inline CLEDController & setDither(uint8_t ditherMode = BINARY_DITHER) { m_DitherMode = ditherMode; return *this; } + /// set the dithering mode for this controller to use + inline CLEDController &setDither(uint8_t ditherMode = BINARY_DITHER) { + m_DitherMode = ditherMode; + return *this; + } + /// get the dithering option currently set for this controller inline uint8_t getDither() { return m_DitherMode; } - /// the the color corrction to use for this controller, expressed as an rgb object - CLEDController & setCorrection(CRGB correction) { m_ColorCorrection = correction; return *this; } + /// the the color corrction to use for this controller, expressed as an rgb object + CLEDController &setCorrection(CRGB correction) { + m_ColorCorrection = correction; + return *this; + } + /// set the color correction to use for this controller - CLEDController & setCorrection(LEDColorCorrection correction) { m_ColorCorrection = correction; return *this; } + CLEDController &setCorrection(LEDColorCorrection correction) { + m_ColorCorrection = correction; + return *this; + } + /// get the correction value used by this controller CRGB getCorrection() { return m_ColorCorrection; } - /// set the color temperature, aka white point, for this controller - CLEDController & setTemperature(CRGB temperature) { m_ColorTemperature = temperature; return *this; } /// set the color temperature, aka white point, for this controller - CLEDController & setTemperature(ColorTemperature temperature) { m_ColorTemperature = temperature; return *this; } + CLEDController &setTemperature(CRGB temperature) { + m_ColorTemperature = temperature; + return *this; + } + + /// set the color temperature, aka white point, for this controller + CLEDController &setTemperature(ColorTemperature temperature) { + m_ColorTemperature = temperature; + return *this; + } + /// get the color temperature, aka whipe point, for this controller CRGB getTemperature() { return m_ColorTemperature; } - /// Get the combined brightness/color adjustment for this controller + /// Get the combined brightness/color adjustment for this controller CRGB getAdjustment(uint8_t scale) { return computeAdjustment(scale, m_ColorCorrection, m_ColorTemperature); } - static CRGB computeAdjustment(uint8_t scale, const CRGB & colorCorrection, const CRGB & colorTemperature) { - #if defined(NO_CORRECTION) && (NO_CORRECTION==1) - return CRGB(scale,scale,scale); - #else - CRGB adj(0,0,0); - - if(scale > 0) { - for(uint8_t i = 0; i < 3; i++) { - uint8_t cc = colorCorrection.raw[i]; - uint8_t ct = colorTemperature.raw[i]; - if(cc > 0 && ct > 0) { - uint32_t work = (((uint32_t)cc)+1) * (((uint32_t)ct)+1) * scale; - work /= 0x10000L; - adj.raw[i] = work & 0xFF; - } - } - } + static CRGB computeAdjustment(uint8_t scale, const CRGB &colorCorrection, const CRGB &colorTemperature) { +#if defined(NO_CORRECTION) && (NO_CORRECTION == 1) + return CRGB(scale,scale,scale); +#else + CRGB adj(0, 0, 0); + + if (scale > 0) { + for (uint8_t i = 0; i < 3; i++) { + uint8_t cc = colorCorrection.raw[i]; + uint8_t ct = colorTemperature.raw[i]; + if (cc > 0 && ct > 0) { + uint32_t work = (((uint32_t) cc) + 1) * (((uint32_t) ct) + 1) * scale; + work /= 0x10000L; + adj.raw[i] = work & 0xFF; + } + } + } - return adj; - #endif + return adj; +#endif } + virtual uint16_t getMaxRefreshRate() const { return 0; } }; // Pixel controller class. This is the class that we use to centralize pixel access in a block of data, including // support for things like RGB reordering, scaling, dithering, skipping (for ARGB data), and eventually, we will // centralize 8/12/16 conversions here as well. -template +template struct PixelController { - const uint8_t *mData; - int mLen,mLenRemaining; - uint8_t d[3]; - uint8_t e[3]; - CRGB mScale; - int8_t mAdvance; - int mOffsets[LANES]; - - PixelController(const PixelController & other) { - d[0] = other.d[0]; - d[1] = other.d[1]; - d[2] = other.d[2]; - e[0] = other.e[0]; - e[1] = other.e[1]; - e[2] = other.e[2]; - mData = other.mData; - mScale = other.mScale; - mAdvance = other.mAdvance; - mLenRemaining = mLen = other.mLen; - for(int i = 0; i < LANES; i++) { mOffsets[i] = other.mOffsets[i]; } - + const uint8_t *mData; + int mLen, mLenRemaining; + uint8_t d[4]; + uint8_t e[4]; + CRGBW mScale; + int8_t mAdvance; + int mOffsets[LANES]; + CRGBW mCurrentPixels[LANES]; // a copy of the current mData... + + bool mUseRgbw; + + PixelController(const PixelController &other) { + d[0] = other.d[0]; + d[1] = other.d[1]; + d[2] = other.d[2]; + d[3] = other.d[3]; + e[0] = other.e[0]; + e[1] = other.e[1]; + e[2] = other.e[2]; + e[3] = other.e[3]; + mData = other.mData; + mScale = other.mScale; + mAdvance = other.mAdvance; + mLenRemaining = mLen = other.mLen; + mUseRgbw = other.mUseRgbw; + for (int i = 0; i < LANES; i++) { + mOffsets[i] = other.mOffsets[i]; + mCurrentPixels[i] = other.mCurrentPixels[i]; } + } - void initOffsets(int len) { - int nOffset = 0; - for(int i = 0; i < LANES; i++) { + void initOffsets(int len) { + int nOffset = 0; + for (int i = 0; i < LANES; i++) { mOffsets[i] = nOffset; - if((1<128) ) #define VIRTUAL_BITS RECOMMENDED_VIRTUAL_BITS - // R is the digther signal 'counter'. - static uint8_t R = 0; - R++; - - // R is wrapped around at 2^ditherBits, - // so if ditherBits is 2, R will cycle through (0,1,2,3) - uint8_t ditherBits = VIRTUAL_BITS; - R &= (0x01 << ditherBits) - 1; - - // Q is the "unscaled dither signal" itself. - // It's initialized to the reversed bits of R. - // If 'ditherBits' is 2, Q here will cycle through (0,128,64,192) - uint8_t Q = 0; - - // Reverse bits in a byte - { - if(R & 0x01) { Q |= 0x80; } - if(R & 0x02) { Q |= 0x40; } - if(R & 0x04) { Q |= 0x20; } - if(R & 0x08) { Q |= 0x10; } - if(R & 0x10) { Q |= 0x08; } - if(R & 0x20) { Q |= 0x04; } - if(R & 0x40) { Q |= 0x02; } - if(R & 0x80) { Q |= 0x01; } - } + // R is the digther signal 'counter'. + static uint8_t R = 0; + R++; + + // R is wrapped around at 2^ditherBits, + // so if ditherBits is 2, R will cycle through (0,1,2,3) + uint8_t ditherBits = VIRTUAL_BITS; + R &= (0x01 << ditherBits) - 1; + + // Q is the "unscaled dither signal" itself. + // It's initialized to the reversed bits of R. + // If 'ditherBits' is 2, Q here will cycle through (0,128,64,192) + uint8_t Q = 0; + + // Reverse bits in a byte + { + if (R & 0x01) { Q |= 0x80; } + if (R & 0x02) { Q |= 0x40; } + if (R & 0x04) { Q |= 0x20; } + if (R & 0x08) { Q |= 0x10; } + if (R & 0x10) { Q |= 0x08; } + if (R & 0x20) { Q |= 0x04; } + if (R & 0x40) { Q |= 0x02; } + if (R & 0x80) { Q |= 0x01; } + } - // Now we adjust Q to fall in the center of each range, - // instead of at the start of the range. - // If ditherBits is 2, Q will be (0, 128, 64, 192) at first, - // and this adjustment makes it (31, 159, 95, 223). - if( ditherBits < 8) { - Q += 0x01 << (7 - ditherBits); - } + // Now we adjust Q to fall in the center of each range, + // instead of at the start of the range. + // If ditherBits is 2, Q will be (0, 128, 64, 192) at first, + // and this adjustment makes it (31, 159, 95, 223). + if (ditherBits < 8) { + Q += 0x01 << (7 - ditherBits); + } - // D and E form the "scaled dither signal" - // which is added to pixel values to affect the - // actual dithering. + // D and E form the "scaled dither signal" + // which is added to pixel values to affect the + // actual dithering. - // Setup the initial D and E values - for(int i = 0; i < 3; i++) { - uint8_t s = mScale.raw[i]; - e[i] = s ? (256/s) + 1 : 0; - d[i] = scale8(Q, e[i]); + // Setup the initial D and E values + uint8_t colorChannels = mUseRgbw ? 4 : 3; + for (int i = 0; i < colorChannels; i++) { + uint8_t s = mScale.raw[i]; + e[i] = s ? (256 / s) + 1 : 0; + d[i] = scale8(Q, e[i]); #if (FASTLED_SCALE8_FIXED == 1) - if(d[i]) (d[i]--); -#endif - if(e[i]) e[i]--; - } + if (d[i]) (d[i]--); #endif + if (e[i]) e[i]--; } +#endif + } + + // Do we have n pixels left to process? + __attribute__((always_inline)) inline bool has(int n) { + return mLenRemaining >= n; + } - // Do we have n pixels left to process? - __attribute__((always_inline)) inline bool has(int n) { - return mLenRemaining >= n; + // toggle dithering enable + void enable_dithering(EDitherMode dither) { + switch (dither) { + case BINARY_DITHER: + init_binary_dithering(); + break; + default: + d[0] = d[1] = d[2] = d[3] = e[0] = e[1] = e[2] = e[3] = 0; + break; } + } - // toggle dithering enable - void enable_dithering(EDitherMode dither) { - switch(dither) { - case BINARY_DITHER: init_binary_dithering(); break; - default: d[0]=d[1]=d[2]=e[0]=e[1]=e[2]=0; break; + __attribute__((always_inline)) inline void setCurrentPixels() { + for (int i = 0; i < LANES; i++) { + if(mUseRgbw) { + mCurrentPixels[i].w = min( + mData[mOffsets[i] + 0], min( + mData[mOffsets[i] + 1], mData[mOffsets[i] + 2])); + + mCurrentPixels[i].r = mData[mOffsets[i] + 0] - mCurrentPixels[i].w; + mCurrentPixels[i].g = mData[mOffsets[i] + 1] - mCurrentPixels[i].w; + mCurrentPixels[i].b = mData[mOffsets[i] + 2] - mCurrentPixels[i].w; + } else { + mCurrentPixels[i].r = mData[mOffsets[i] + 0]; + mCurrentPixels[i].g = mData[mOffsets[i] + 1]; + mCurrentPixels[i].b = mData[mOffsets[i] + 2]; + mCurrentPixels[i].w = mData[mOffsets[i] + 3]; } } + } - __attribute__((always_inline)) inline int size() { return mLen; } + __attribute__((always_inline)) inline int size() { return mLen; } - // get the amount to advance the pointer by - __attribute__((always_inline)) inline int advanceBy() { return mAdvance; } + // get the amount to advance the pointer by + __attribute__((always_inline)) inline int advanceBy() { return mAdvance; } - // advance the data pointer forward, adjust position counter - __attribute__((always_inline)) inline void advanceData() { mData += mAdvance; mLenRemaining--;} + // advance the data pointer forward, adjust position counter + __attribute__((always_inline)) inline void advanceData() { + mData += mAdvance; + mLenRemaining--; + setCurrentPixels(); + } - // step the dithering forward - __attribute__((always_inline)) inline void stepDithering() { - // IF UPDATING HERE, BE SURE TO UPDATE THE ASM VERSION IN - // clockless_trinket.h! - d[0] = e[0] - d[0]; - d[1] = e[1] - d[1]; - d[2] = e[2] - d[2]; - } + // step the dithering forward + __attribute__((always_inline)) inline void stepDithering() { + // IF UPDATING HERE, BE SURE TO UPDATE THE ASM VERSION IN + // clockless_trinket.h! + d[0] = e[0] - d[0]; + d[1] = e[1] - d[1]; + d[2] = e[2] - d[2]; + d[3] = e[3] - d[3]; + } - // Some chipsets pre-cycle the first byte, which means we want to cycle byte 0's dithering separately - __attribute__((always_inline)) inline void preStepFirstByteDithering() { - d[RO(0)] = e[RO(0)] - d[RO(0)]; - } + // Some chipsets pre-cycle the first byte, which means we want to cycle byte 0's dithering separately + __attribute__((always_inline)) inline void preStepFirstByteDithering() { + d[RO(0)] = e[RO(0)] - d[RO(0)]; + } + + template + __attribute__((always_inline)) inline static uint8_t loadByte(PixelController &pc) { + //Serial.printf("%0x, %0x, %0x\n", SLOT, RO(SLOT), pc.mCurrentPixels[0].raw[RO(SLOT)]); + return pc.mCurrentPixels[0].raw[RO(SLOT)]; + } + + template + __attribute__((always_inline)) inline static uint8_t loadByte(PixelController &pc, int lane) { + return pc.mCurrentPixels[lane].raw[RO(SLOT)]; + } + + template + __attribute__((always_inline)) inline static uint8_t dither(PixelController &pc, uint8_t b) { + return b ? qadd8(b, pc.d[RO(SLOT)]) : 0; + } + + template + __attribute__((always_inline)) inline static uint8_t dither(PixelController &, uint8_t b, uint8_t d) { + return b ? qadd8(b, d) : 0; + } + + template + __attribute__((always_inline)) inline static uint8_t scale(PixelController &pc, uint8_t b) { + //Serial.printf("%0x, %0x, %0x, %0x\n", SLOT, RO(SLOT), b, pc.mScale.raw[RO(SLOT)]); + + return scale8(b, pc.mScale.raw[RO(SLOT)]); + } + + template + __attribute__((always_inline)) inline static uint8_t + scale(PixelController &, uint8_t b, uint8_t scale) { return scale8(b, scale); } + + // composite shortcut functions for loading, dithering, and scaling + template + __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController &pc) { + return scale(pc, pc.dither(pc, pc.loadByte(pc))); + } + + template + __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController &pc, int lane) { + return scale(pc, pc.dither(pc, pc.loadByte(pc, lane))); + } + + template + __attribute__((always_inline)) inline static uint8_t + loadAndScale(PixelController &pc, int lane, uint8_t d, uint8_t scale) { + return scale8(pc.dither(pc, pc.loadByte(pc, lane), d), scale); + } + + template + __attribute__((always_inline)) inline static uint8_t + loadAndScale(PixelController &pc, int lane, uint8_t scale) { return scale8(pc.loadByte(pc, lane), scale); } + + template + __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController &pc) { + pc.advanceData(); + return pc.loadAndScale(pc); + } + + template + __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController &pc, int lane) { + pc.advanceData(); + return pc.loadAndScale(pc, lane); + } + + template + __attribute__((always_inline)) inline static uint8_t getd(PixelController &pc) { return pc.d[RO(SLOT)]; } + + template + __attribute__((always_inline)) inline static uint8_t getscale(PixelController &pc) { + return pc.mScale.raw[RO(SLOT)]; + } + + // Helper functions to get around gcc stupidities + __attribute__((always_inline)) inline uint8_t loadAndScale0(int lane) { return loadAndScale<0>(*this, lane); } + + __attribute__((always_inline)) inline uint8_t loadAndScale1(int lane) { return loadAndScale<1>(*this, lane); } - template __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc) { return pc.mData[RO(SLOT)]; } - template __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc, int lane) { return pc.mData[pc.mOffsets[lane] + RO(SLOT)]; } + __attribute__((always_inline)) inline uint8_t loadAndScale2(int lane) { return loadAndScale<2>(*this, lane); } - template __attribute__((always_inline)) inline static uint8_t dither(PixelController & pc, uint8_t b) { return b ? qadd8(b, pc.d[RO(SLOT)]) : 0; } - template __attribute__((always_inline)) inline static uint8_t dither(PixelController & , uint8_t b, uint8_t d) { return b ? qadd8(b,d) : 0; } + __attribute__((always_inline)) inline uint8_t loadAndScale3(int lane) { return loadAndScale<3>(*this, lane); } - template __attribute__((always_inline)) inline static uint8_t scale(PixelController & pc, uint8_t b) { return scale8(b, pc.mScale.raw[RO(SLOT)]); } - template __attribute__((always_inline)) inline static uint8_t scale(PixelController & , uint8_t b, uint8_t scale) { return scale8(b, scale); } + __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane) { + return advanceAndLoadAndScale<0>(*this, lane); + } + + __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane) { + stepDithering(); + return advanceAndLoadAndScale<0>(*this, lane); + } - // composite shortcut functions for loading, dithering, and scaling - template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc) { return scale(pc, pc.dither(pc, pc.loadByte(pc))); } - template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane) { return scale(pc, pc.dither(pc, pc.loadByte(pc, lane))); } - template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t d, uint8_t scale) { return scale8(pc.dither(pc, pc.loadByte(pc, lane), d), scale); } - template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t scale) { return scale8(pc.loadByte(pc, lane), scale); } + __attribute__((always_inline)) inline uint8_t loadAndScale0() { return loadAndScale<0>(*this); } - template __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc) { pc.advanceData(); return pc.loadAndScale(pc); } - template __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane) { pc.advanceData(); return pc.loadAndScale(pc, lane); } + __attribute__((always_inline)) inline uint8_t loadAndScale1() { return loadAndScale<1>(*this); } - template __attribute__((always_inline)) inline static uint8_t getd(PixelController & pc) { return pc.d[RO(SLOT)]; } - template __attribute__((always_inline)) inline static uint8_t getscale(PixelController & pc) { return pc.mScale.raw[RO(SLOT)]; } + __attribute__((always_inline)) inline uint8_t loadAndScale2() { return loadAndScale<2>(*this); } - // Helper functions to get around gcc stupidities - __attribute__((always_inline)) inline uint8_t loadAndScale0(int lane) { return loadAndScale<0>(*this, lane); } - __attribute__((always_inline)) inline uint8_t loadAndScale1(int lane) { return loadAndScale<1>(*this, lane); } - __attribute__((always_inline)) inline uint8_t loadAndScale2(int lane) { return loadAndScale<2>(*this, lane); } - __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane) { return advanceAndLoadAndScale<0>(*this, lane); } - __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane); } + __attribute__((always_inline)) inline uint8_t loadAndScale3() { return loadAndScale<3>(*this); } - __attribute__((always_inline)) inline uint8_t loadAndScale0() { return loadAndScale<0>(*this); } - __attribute__((always_inline)) inline uint8_t loadAndScale1() { return loadAndScale<1>(*this); } - __attribute__((always_inline)) inline uint8_t loadAndScale2() { return loadAndScale<2>(*this); } - __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0() { return advanceAndLoadAndScale<0>(*this); } - __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0() { stepDithering(); return advanceAndLoadAndScale<0>(*this); } + __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0() { return advanceAndLoadAndScale<0>(*this); } + + __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0() { + stepDithering(); + return advanceAndLoadAndScale<0>(*this); + } }; -template class CPixelLEDController : public CLEDController { +template +class CPixelLEDController : public CLEDController { protected: - virtual void showPixels(PixelController & pixels) = 0; + virtual void showPixels(PixelController &pixels) = 0; - /// set all the leds on the controller to a given color - ///@param data the crgb color to set the leds to - ///@param nLeds the numner of leds to set to this color - ///@param scale the rgb scaling value for outputting color - virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) { - PixelController pixels(data, nLeds, scale, getDither()); - showPixels(pixels); - } + /// set all the leds on the controller to a given color + ///@param data the crgb color to set the leds to + ///@param nLeds the numner of leds to set to this color + ///@param scale the rgb scaling value for outputting color + virtual void showColor(const struct CRGB &data, int nLeds, CRGB scale) { + PixelController pixels(data, nLeds, scale, getDither(), USE_RGBW); + showPixels(pixels); + } /// write the passed in rgb data out to the leds managed by this controller ///@param data the rgb data to write out to the strip ///@param nLeds the number of leds being written out ///@param scale the rgb scaling to apply to each led before writing it out - virtual void show(const struct CRGB *data, int nLeds, CRGB scale) { - PixelController pixels(data, nLeds, scale, getDither()); - showPixels(pixels); - } + virtual void show(const struct CRGB *data, int nLeds, CRGB scale) { + PixelController pixels(data, nLeds, scale, getDither(), USE_RGBW); + showPixels(pixels); + } public: - CPixelLEDController() : CLEDController() {} + CPixelLEDController() : CLEDController() {} }; diff --git a/pixeltypes.h b/pixeltypes.h index 075c7945a9..a2aab8acfc 100644 --- a/pixeltypes.h +++ b/pixeltypes.h @@ -7,8 +7,12 @@ #include "lib8tion.h" #include "color.h" +#define MIN(X,Y) (((X)<(Y)) ? (X):(Y)) +#define MAX(A, B) (( (A) > (B) ) ? (A) : (B)) + FASTLED_NAMESPACE_BEGIN +struct CRGBW; struct CRGB; struct CHSV; @@ -748,7 +752,6 @@ struct CRGB { } HTMLColorCode; }; - inline __attribute__((always_inline)) bool operator== (const CRGB& lhs, const CRGB& rhs) { return (lhs.r == rhs.r) && (lhs.g == rhs.g) && (lhs.b == rhs.b); @@ -848,6 +851,89 @@ inline CRGB operator%( const CRGB& p1, uint8_t d) } +struct CRGBW { + union { + struct { + union { + uint8_t r; + uint8_t red; + }; + union { + uint8_t g; + uint8_t green; + }; + union { + uint8_t b; + uint8_t blue; + }; + union { + uint8_t w; + uint8_t white; + }; + }; + uint8_t raw[4]; + }; + + // default values are UNINITIALIZED + inline CRGBW() __attribute__((always_inline)) + { + } + + /// allow construction from R, G, B, W + inline CRGBW( uint8_t ir, uint8_t ig, uint8_t ib, uint8_t iw) __attribute__((always_inline)) + : r(ir), g(ig), b(ib), w(iw) + { + } + + /// allow assignment from one RGB struct to another + inline CRGBW& operator= (const CRGBW& rhs) __attribute__((always_inline)) + { + r = rhs.r; + g = rhs.g; + b = rhs.b; + w = rhs.w; + return *this; + } + + /// allow construction from R, G, B + inline CRGBW( uint8_t ir, uint8_t ig, uint8_t ib, bool makeWhite = false) __attribute__((always_inline)) + { + if(makeWhite) { + w = MIN(ir, ig); + w = MIN(ib, w); + } else { + w = 0; + } + r = ir - w; + g = ig - w; + b = ib - w; + //Serial.printf("%0x,%0x,%0x -> %0x,%0x,%0x,%0x: %0x\n", ir, ig, ib, r,g,b,w, makeWhite); + } + + + /// come from CRGB + inline CRGBW(const CRGB& rhs, bool makeWhite = false) __attribute__((always_inline)) + { + if(makeWhite) { + w = MIN(rhs.r, rhs.g); + w = MIN(rhs.b, w); + } else { + w = 0; + } + r = rhs.r - w; + g = rhs.g - w; + b = rhs.b - w; + } + +}; + +__attribute__((always_inline)) +inline CRGBW makeScaleStruct( const CRGB& scale, bool useWhite = false) +{ + CRGBW s = CRGBW(scale.r, scale.g, scale.b, false); + s.w = useWhite ? MAX(scale.r, MAX(scale.g, scale.b)) : 0; + return s; +} /// RGB orderings, used when instantiating controllers to determine what /// order the controller should send RGB data out in, RGB being the default diff --git a/platforms/esp/8266/clockless_block_esp8266.h b/platforms/esp/8266/clockless_block_esp8266.h index e3341d1613..9587f9787f 100644 --- a/platforms/esp/8266/clockless_block_esp8266.h +++ b/platforms/esp/8266/clockless_block_esp8266.h @@ -17,8 +17,8 @@ extern uint32_t _frame_cnt; extern uint32_t _retry_cnt; #endif -template -class InlineBlockClocklessController : public CPixelLEDController { +template +class InlineBlockClocklessController : public CPixelLEDController { typedef typename FastPin::port_ptr_t data_ptr_t; typedef typename FastPin::port_t data_t; @@ -52,6 +52,7 @@ class InlineBlockClocklessController : public CPixelLEDController, initPin<13>, initPin<14>, initPin<15>, initPin<4>, initPin<5>}; for (uint8_t i = 0; i < USED_LANES; ++i) { @@ -127,6 +128,12 @@ class InlineBlockClocklessController : public CPixelLEDController(last_mark, b0, allpixels); + + if(allpixels.mUseRgbw) { + // Write third byte, read 4rd byte + writeBits<8+XTRA0,3>(last_mark, b0, allpixels); + } + allpixels.advanceData(); // Write third byte diff --git a/platforms/esp/8266/clockless_esp8266.h b/platforms/esp/8266/clockless_esp8266.h index f8d9d8de3e..11f1d22539 100644 --- a/platforms/esp/8266/clockless_esp8266.h +++ b/platforms/esp/8266/clockless_esp8266.h @@ -7,11 +7,14 @@ extern uint32_t _frame_cnt; extern uint32_t _retry_cnt; #endif +#ifdef FASTLED_RGBW + #ifdef FASTLED_HAS_PRAGMA_MESSAGE # pragma message "Using RGBW controller as per FASTLED_RGBW define" #else # warning "Using RGBW controller as per FASTLED_RGBW define" #endif +#endif // Info on reading cycle counter from https://github.com/kbeckmann/nodemcu-firmware/blob/ws2812-dual/app/modules/ws2812.c __attribute__ ((always_inline)) inline static uint32_t __clock_cycles() { @@ -31,8 +34,8 @@ typedef struct { #define FASTLED_HAS_CLOCKLESS 1 -template -class ClocklessController : public CPixelLEDController { +template +class ClocklessController : public CPixelLEDController { typedef typename FastPin::port_ptr_t data_ptr_t; typedef typename FastPin::port_t data_t; @@ -126,16 +129,29 @@ class ClocklessController : public CPixelLEDController { output.c0 = pixels.advanceAndLoadAndScale0(); #else + //Serial.printf("0 - %0x\n", b); // Write first byte, read next byte writeBits<8+XTRA0>(last_mark, b); b = pixels.loadAndScale1(); + //Serial.printf("1 - %0x\n", b); // Write second byte, read 3rd byte writeBits<8+XTRA0>(last_mark, b); b = pixels.loadAndScale2(); + //Serial.printf("2 - %0x\n", b); + if(pixels.mUseRgbw) { + // Write third byte, read 4rd byte + writeBits<8 + XTRA0>(last_mark, b); + b = pixels.loadAndScale3(); + } + + //Serial.printf("3 - %0x\n", b); // Write third byte, read 1st byte of next pixel writeBits<8+XTRA0>(last_mark, b); + + + b = pixels.advanceAndLoadAndScale0(); #endif // FASTLED_RGBW diff --git a/platforms/esp/8266/clockless_esp8266_dma.h b/platforms/esp/8266/clockless_esp8266_dma.h index 091cf0bc10..2b494ccd29 100644 --- a/platforms/esp/8266/clockless_esp8266_dma.h +++ b/platforms/esp/8266/clockless_esp8266_dma.h @@ -2,10 +2,11 @@ FASTLED_NAMESPACE_BEGIN +// TODO: FIX THIS TOO! #ifdef FASTLED_RGBW #define BYTES_PER_ELEMENT 4 #else -#define BYTES_PER_ELEMENT 3 +#define BYTES_PER_ELEMENT 4 #endif #ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES @@ -104,7 +105,7 @@ typedef struct { #define FASTLED_HAS_CLOCKLESS 1 -template +template class ClocklessController : public CPixelLEDController { typedef typename FastPin::port_ptr_t data_ptr_t; typedef typename FastPin::port_t data_t; @@ -332,6 +333,11 @@ class ClocklessController : public CPixelLEDController { b = pixels.loadAndScale2(); pDma = writeBits(pDma, b); + if(USE_RGBW) { + b = pixels.loadAndScale3(); + pDma = writeBits(pDma, b); + } + b = pixels.advanceAndLoadAndScale0(); #endif // FASTLED_RGBW @@ -423,7 +429,7 @@ class ClocklessController : public CPixelLEDController { } }; -template -ClocklessController* ClocklessController::s_this; +template +ClocklessController* ClocklessController::s_this; FASTLED_NAMESPACE_END