#pragma once #ifndef __INC_CHIPSETS_H #define __INC_CHIPSETS_H #include "pixeltypes.h" #include "fl/five_bit_hd_gamma.h" #include "fl/force_inline.h" #include "fl/bit_cast.h" #include "pixel_iterator.h" #include "crgb.h" #include "eorder.h" #include "fl/namespace.h" #include "fl/math_macros.h" // Conditional namespace handling for WASM builds #ifdef FASTLED_FORCE_NAMESPACE #define FASTLED_CLOCKLESS_CONTROLLER fl::ClocklessController #else #define FASTLED_CLOCKLESS_CONTROLLER ClocklessController #endif #ifndef FASTLED_CLOCKLESS_USES_NANOSECONDS #if defined(FASTLED_TEENSY4) #define FASTLED_CLOCKLESS_USES_NANOSECONDS 1 #elif defined(ESP32) #include "third_party/espressif/led_strip/src/enabled.h" // RMT 5.1 driver converts from nanoseconds to RMT ticks. #if FASTLED_RMT5 #define FASTLED_CLOCKLESS_USES_NANOSECONDS 1 #else #define FASTLED_CLOCKLESS_USES_NANOSECONDS 0 #endif #else #define FASTLED_CLOCKLESS_USES_NANOSECONDS 0 #endif // FASTLED_TEENSY4 #endif // FASTLED_CLOCKLESS_USES_NANOSECONDS // Allow overclocking of the clockless family of leds. 1.2 would be // 20% overclocking. In tests WS2812 can be overclocked at 20%, but // various manufacturers may be different. This is a global value // which is overridable by each supported chipset. #ifdef FASTLED_LED_OVERCLOCK #warning "FASTLED_LED_OVERCLOCK has been changed to FASTLED_OVERCLOCK. Please update your code." #define FASTLED_OVERCLOCK FASTLED_LED_OVERCLOCK #endif #ifndef FASTLED_OVERCLOCK #define FASTLED_OVERCLOCK 1.0 #else #ifndef FASTLED_OVERCLOCK_SUPPRESS_WARNING #warning "FASTLED_OVERCLOCK is now active, #define FASTLED_OVERCLOCK_SUPPRESS_WARNING to disable this warning" #endif #endif // So many platforms have specialized WS2812 controllers. Why? Because they // are the cheapest chipsets use. So we special case this. #include "platforms/chipsets_specialized_ws2812.h" /// @file chipsets.h /// Contains the bulk of the definitions for the various LED chipsets supported. /// @defgroup Chipsets LED Chipset Controllers /// Implementations of ::CLEDController classes for various led chipsets. /// /// @{ #if defined(ARDUINO) //&& defined(SoftwareSerial_h) #if defined(SoftwareSerial_h) || defined(__SoftwareSerial_h) #include #define HAS_PIXIE FASTLED_NAMESPACE_BEGIN /// Adafruit Pixie controller class /// @tparam DATA_PIN the pin to write data out on /// @tparam RGB_ORDER the RGB ordering for the LED data template class PixieController : public CPixelLEDController { SoftwareSerial Serial; CMinWait<2000> mWait; public: PixieController() : Serial(-1, DATA_PIN) {} protected: /// Initialize the controller virtual void init() { Serial.begin(115200); mWait.mark(); } /// @copydoc CPixelLEDController::showPixels() virtual void showPixels(PixelController & pixels) { mWait.wait(); while(pixels.has(1)) { fl::u8 r = pixels.loadAndScale0(); Serial.write(r); fl::u8 g = pixels.loadAndScale1(); Serial.write(g); fl::u8 b = pixels.loadAndScale2(); Serial.write(b); pixels.advanceData(); pixels.stepDithering(); } mWait.mark(); } }; // template // class PixieController : public PixieBaseController { // public: // virtual void init() { // STREAM.begin(115200); // } // }; FASTLED_NAMESPACE_END #endif #endif // Emulution layer to support RGBW leds on RGB controllers. This works by creating // a side buffer dedicated for the RGBW data. The RGB data is then converted to RGBW // and sent to the delegate controller for rendering as if it were RGB data. FASTLED_NAMESPACE_BEGIN template < typename CONTROLLER, EOrder RGB_ORDER = GRB> // Default on WS2812> class RGBWEmulatedController : public CPixelLEDController { public: // ControllerT is a helper class. It subclasses the device controller class // and has three methods to call the three protected methods we use. // This is janky, but redeclaring public methods protected in a derived class // is janky, too. // N.B., byte order must be RGB. typedef CONTROLLER ControllerBaseT; class ControllerT : public CONTROLLER { friend class RGBWEmulatedController; void *callBeginShowLeds(int size) { return ControllerBaseT::beginShowLeds(size); } void callShow(CRGB *data, int nLeds, fl::u8 brightness) { ControllerBaseT::show(data, nLeds, brightness); } void callEndShowLeds(void *data) { ControllerBaseT::endShowLeds(data); } }; static const int LANES = CONTROLLER::LANES_VALUE; static const uint32_t MASK = CONTROLLER::MASK_VALUE; // The delegated controller must do no reordering. static_assert(RGB == CONTROLLER::RGB_ORDER_VALUE, "The delegated controller MUST NOT do reordering"); RGBWEmulatedController(const Rgbw& rgbw = RgbwDefault()) { this->setRgbw(rgbw); }; ~RGBWEmulatedController() { delete[] mRGBWPixels; } virtual void *beginShowLeds(int size) override { return mController.callBeginShowLeds(Rgbw::size_as_rgb(size)); } virtual void endShowLeds(void *data) override { return mController.callEndShowLeds(data); } virtual void showPixels(PixelController &pixels) override { // Ensure buffer is large enough ensureBuffer(pixels.size()); Rgbw rgbw = this->getRgbw(); fl::u8 *data = fl::bit_cast_ptr(mRGBWPixels); while (pixels.has(1)) { pixels.stepDithering(); pixels.loadAndScaleRGBW(rgbw, data, data + 1, data + 2, data + 3); data += 4; pixels.advanceData(); } // Force the device controller to a state where it passes data through // unmodified: color correction, color temperature, dither, and brightness // (passed as an argument to show()). Temporarily enable the controller, // show the LEDs, and disable it again. // // The device controller is in the global controller list, so if we // don't keep it disabled, it will refresh again with unknown brightness, // temperature, etc. mController.setCorrection(CRGB(255, 255, 255)); mController.setTemperature(CRGB(255, 255, 255)); mController.setDither(DISABLE_DITHER); mController.setEnabled(true); mController.callShow(mRGBWPixels, Rgbw::size_as_rgb(pixels.size()), 255); mController.setEnabled(false); } private: // Needed by the interface. void init() override { mController.init(); mController.setEnabled(false); } void ensureBuffer(int32_t num_leds) { if (num_leds != mNumRGBLeds) { mNumRGBLeds = num_leds; // The delegate controller expects the raw pixel byte data in multiples of 3. // In the case of src data not a multiple of 3, then we need to // add pad bytes so that the delegate controller doesn't walk off the end // of the array and invoke a buffer overflow panic. uint32_t new_size = Rgbw::size_as_rgb(num_leds); delete[] mRGBWPixels; mRGBWPixels = new CRGB[new_size]; // showPixels may never clear the last two pixels. for (uint32_t i = 0; i < new_size; i++) { mRGBWPixels[i] = CRGB(0, 0, 0); } mController.setLeds(mRGBWPixels, new_size); } } CRGB *mRGBWPixels = nullptr; int32_t mNumRGBLeds = 0; int32_t mNumRGBWLeds = 0; ControllerT mController; // Real controller. }; /// @defgroup ClockedChipsets Clocked Chipsets /// Nominally SPI based, these chipsets have a data and a clock line. /// @{ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // LPD8806 controller class - takes data/clock/select pin values (N.B. should take an SPI definition?) // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// LPD8806 controller class. /// @tparam DATA_PIN the data pin for these LEDs /// @tparam CLOCK_PIN the clock pin for these LEDs /// @tparam RGB_ORDER the RGB ordering for these LEDs /// @tparam SPI_SPEED the clock divider used for these LEDs. Set using the ::DATA_RATE_MHZ / ::DATA_RATE_KHZ macros. Defaults to ::DATA_RATE_MHZ(12) template class LPD8806Controller : public CPixelLEDController { typedef SPIOutput SPI; class LPD8806_ADJUST { public: // LPD8806 spec wants the high bit of every rgb data byte sent out to be set. FASTLED_FORCE_INLINE static fl::u8 adjust(FASTLED_REGISTER fl::u8 data) { return ((data>>1) | 0x80) + ((data && (data<254)) & 0x01); } FASTLED_FORCE_INLINE static void postBlock(int len, void* context = NULL) { SPI* pSPI = static_cast(context); pSPI->writeBytesValueRaw(0, ((len*3+63)>>6)); } }; SPI mSPI; public: LPD8806Controller() {} virtual void init() { mSPI.init(); } protected: /// @copydoc CPixelLEDController::showPixels() virtual void showPixels(PixelController & pixels) { mSPI.template writePixels<0, LPD8806_ADJUST, RGB_ORDER>(pixels, &mSPI); } }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // WS2801 definition - takes data/clock/select pin values (N.B. should take an SPI definition?) // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// WS2801 controller class. /// @tparam DATA_PIN the data pin for these LEDs /// @tparam CLOCK_PIN the clock pin for these LEDs /// @tparam RGB_ORDER the RGB ordering for these LEDs /// @tparam SPI_SPEED the clock divider used for these LEDs. Set using the ::DATA_RATE_MHZ / ::DATA_RATE_KHZ macros. Defaults to ::DATA_RATE_MHZ(1) template class WS2801Controller : public CPixelLEDController { typedef SPIOutput SPI; SPI mSPI; CMinWait<1000> mWaitDelay; public: WS2801Controller() {} /// Initialize the controller virtual void init() { mSPI.init(); mWaitDelay.mark(); } protected: /// @copydoc CPixelLEDController::showPixels() virtual void showPixels(PixelController & pixels) { mWaitDelay.wait(); mSPI.template writePixels<0, DATA_NOP, RGB_ORDER>(pixels, NULL); mWaitDelay.mark(); } }; /// WS2803 controller class. /// @copydetails WS2801Controller template class WS2803Controller : public WS2801Controller {}; /// LPD6803 controller class (LPD1101). /// 16 bit (1 bit const "1", 5 bit red, 5 bit green, 5 bit blue). /// In chip CMODE pin must be set to 1 (inside oscillator mode). /// @tparam DATA_PIN the data pin for these LEDs /// @tparam CLOCK_PIN the clock pin for these LEDs /// @tparam RGB_ORDER the RGB ordering for these LEDs /// @tparam SPI_SPEED the clock divider used for these LEDs. Set using the ::DATA_RATE_MHZ / ::DATA_RATE_KHZ macros. Defaults to ::DATA_RATE_MHZ(12) /// @see Datasheet: https://cdn-shop.adafruit.com/datasheets/LPD6803.pdf template class LPD6803Controller : public CPixelLEDController { typedef SPIOutput SPI; SPI mSPI; void startBoundary() { mSPI.writeByte(0); mSPI.writeByte(0); mSPI.writeByte(0); mSPI.writeByte(0); } void endBoundary(int nLeds) { int nDWords = (nLeds/32); do { mSPI.writeByte(0xFF); mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); } while(nDWords--); } public: LPD6803Controller() {} virtual void init() { mSPI.init(); } protected: /// @copydoc CPixelLEDController::showPixels() virtual void showPixels(PixelController & pixels) { mSPI.select(); startBoundary(); while(pixels.has(1)) { FASTLED_REGISTER fl::u16 command; command = 0x8000; command |= (pixels.loadAndScale0() & 0xF8) << 7; // red is the high 5 bits command |= (pixels.loadAndScale1() & 0xF8) << 2; // green is the middle 5 bits mSPI.writeByte((command >> 8) & 0xFF); command |= pixels.loadAndScale2() >> 3 ; // blue is the low 5 bits mSPI.writeByte(command & 0xFF); pixels.stepDithering(); pixels.advanceData(); } endBoundary(pixels.size()); mSPI.waitFully(); mSPI.release(); } }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // APA102 definition - takes data/clock/select pin values (N.B. should take an SPI definition?) // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// APA102 controller class. /// @tparam DATA_PIN the data pin for these LEDs /// @tparam CLOCK_PIN the clock pin for these LEDs /// @tparam RGB_ORDER the RGB ordering for these LEDs /// @tparam SPI_SPEED the clock divider used for these LEDs. Set using the ::DATA_RATE_MHZ / ::DATA_RATE_KHZ macros. Defaults to ::DATA_RATE_MHZ(12) template < fl::u8 DATA_PIN, fl::u8 CLOCK_PIN, EOrder RGB_ORDER = RGB, // APA102 has a bug where long strip can't handle full speed due to clock degredation. // This only affects long strips, but then again if you have a short strip does 6 mhz actually slow // you down? Probably not. And you can always bump it up for speed. Therefore we are prioritizing // "just works" over "fastest possible" here. // https://www.pjrc.com/why-apa102-leds-have-trouble-at-24-mhz/ uint32_t SPI_SPEED = DATA_RATE_MHZ(6), fl::FiveBitGammaCorrectionMode GAMMA_CORRECTION_MODE = fl::kFiveBitGammaCorrectionMode_Null, uint32_t START_FRAME = 0x00000000, uint32_t END_FRAME = 0xFF000000 > class APA102Controller : public CPixelLEDController { typedef SPIOutput SPI; SPI mSPI; void startBoundary() { mSPI.writeWord(START_FRAME >> 16); mSPI.writeWord(START_FRAME & 0xFFFF); } void endBoundary(int nLeds) { int nDWords = (nLeds/32); const fl::u8 b0 = fl::u8(END_FRAME >> 24 & 0x000000ff); const fl::u8 b1 = fl::u8(END_FRAME >> 16 & 0x000000ff); const fl::u8 b2 = fl::u8(END_FRAME >> 8 & 0x000000ff); const fl::u8 b3 = fl::u8(END_FRAME >> 0 & 0x000000ff); do { mSPI.writeByte(b0); mSPI.writeByte(b1); mSPI.writeByte(b2); mSPI.writeByte(b3); } while(nDWords--); } FASTLED_FORCE_INLINE void writeLed(fl::u8 brightness, fl::u8 b0, fl::u8 b1, fl::u8 b2) { #ifdef FASTLED_SPI_BYTE_ONLY mSPI.writeByte(0xE0 | brightness); mSPI.writeByte(b0); mSPI.writeByte(b1); mSPI.writeByte(b2); #else fl::u16 b = 0xE000 | (brightness << 8) | (fl::u16)b0; mSPI.writeWord(b); fl::u16 w = b1 << 8; w |= b2; mSPI.writeWord(w); #endif } FASTLED_FORCE_INLINE void write2Bytes(fl::u8 b1, fl::u8 b2) { #ifdef FASTLED_SPI_BYTE_ONLY mSPI.writeByte(b1); mSPI.writeByte(b2); #else mSPI.writeWord(fl::u16(b1) << 8 | b2); #endif } public: APA102Controller() {} virtual void init() override { mSPI.init(); } protected: /// @copydoc CPixelLEDController::showPixels() virtual void showPixels(PixelController & pixels) override { switch (GAMMA_CORRECTION_MODE) { case fl::kFiveBitGammaCorrectionMode_Null: { showPixelsDefault(pixels); break; } case fl::kFiveBitGammaCorrectionMode_BitShift: { showPixelsGammaBitShift(pixels); break; } } } private: static inline void getGlobalBrightnessAndScalingFactors( PixelController & pixels, fl::u8* out_s0, fl::u8* out_s1, fl::u8* out_s2, fl::u8* out_brightness) { #if FASTLED_HD_COLOR_MIXING fl::u8 brightness; pixels.getHdScale(out_s0, out_s1, out_s2, &brightness); struct Math { static fl::u16 map(fl::u16 x, fl::u16 in_min, fl::u16 in_max, fl::u16 out_min, fl::u16 out_max) { const fl::u16 run = in_max - in_min; const fl::u16 rise = out_max - out_min; const fl::u16 delta = x - in_min; return (delta * rise) / run + out_min; } }; // *out_brightness = Math::map(brightness, 0, 255, 0, 31); fl::u16 bri = Math::map(brightness, 0, 255, 0, 31); if (bri == 0 && brightness != 0) { // Fixes https://github.com/FastLED/FastLED/issues/1908 bri = 1; } *out_brightness = static_cast(bri); return; #else fl::u8 s0, s1, s2; pixels.loadAndScaleRGB(&s0, &s1, &s2); #if FASTLED_USE_GLOBAL_BRIGHTNESS == 1 // This function is pure magic. const fl::u16 maxBrightness = 0x1F; fl::u16 brightness = ((((fl::u16)MAX(MAX(s0, s1), s2) + 1) * maxBrightness - 1) >> 8) + 1; s0 = (maxBrightness * s0 + (brightness >> 1)) / brightness; s1 = (maxBrightness * s1 + (brightness >> 1)) / brightness; s2 = (maxBrightness * s2 + (brightness >> 1)) / brightness; #else const fl::u8 brightness = 0x1F; #endif // FASTLED_USE_GLOBAL_BRIGHTNESS *out_s0 = s0; *out_s1 = s1; *out_s2 = s2; *out_brightness = static_cast(brightness); #endif // FASTLED_HD_COLOR_MIXING } // Legacy showPixels implementation. inline void showPixelsDefault(PixelController & pixels) { mSPI.select(); fl::u8 s0, s1, s2, global_brightness; getGlobalBrightnessAndScalingFactors(pixels, &s0, &s1, &s2, &global_brightness); startBoundary(); while (pixels.has(1)) { fl::u8 c0, c1, c2; pixels.loadAndScaleRGB(&c0, &c1, &c2); writeLed(global_brightness, c0, c1, c2); pixels.stepDithering(); pixels.advanceData(); } endBoundary(pixels.size()); mSPI.waitFully(); mSPI.release(); } inline void showPixelsGammaBitShift(PixelController & pixels) { mSPI.select(); startBoundary(); while (pixels.has(1)) { // Load raw uncorrected r,g,b values. fl::u8 brightness, c0, c1, c2; // c0-c2 is the RGB data re-ordered for pixel pixels.loadAndScale_APA102_HD(&c0, &c1, &c2, &brightness); writeLed(brightness, c0, c1, c2); pixels.stepDithering(); pixels.advanceData(); } endBoundary(pixels.size()); mSPI.waitFully(); mSPI.release(); } }; /// APA102 high definition controller class. /// @tparam DATA_PIN the data pin for these LEDs /// @tparam CLOCK_PIN the clock pin for these LEDs /// @tparam RGB_ORDER the RGB ordering for these LEDs /// @tparam SPI_SPEED the clock divider used for these LEDs. Set using the ::DATA_RATE_MHZ / ::DATA_RATE_KHZ macros. Defaults to ::DATA_RATE_MHZ(24) template < fl::u8 DATA_PIN, fl::u8 CLOCK_PIN, EOrder RGB_ORDER = RGB, // APA102 has a bug where long strip can't handle full speed due to clock degredation. // This only affects long strips, but then again if you have a short strip does 6 mhz actually slow // you down? Probably not. And you can always bump it up for speed. Therefore we are prioritizing // "just works" over "fastest possible" here. // https://www.pjrc.com/why-apa102-leds-have-trouble-at-24-mhz/ uint32_t SPI_SPEED = DATA_RATE_MHZ(6) > class APA102ControllerHD : public APA102Controller< DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED, fl::kFiveBitGammaCorrectionMode_BitShift, uint32_t(0x00000000), uint32_t(0x00000000)> { public: APA102ControllerHD() = default; APA102ControllerHD(const APA102ControllerHD&) = delete; }; /// SK9822 controller class. It's exactly the same as the APA102Controller protocol but with a different END_FRAME and default SPI_SPEED. /// @tparam DATA_PIN the data pin for these LEDs /// @tparam CLOCK_PIN the clock pin for these LEDs /// @tparam RGB_ORDER the RGB ordering for these LEDs /// @tparam SPI_SPEED the clock divider used for these LEDs. Set using the ::DATA_RATE_MHZ / ::DATA_RATE_KHZ macros. Defaults to ::DATA_RATE_MHZ(24) template < fl::u8 DATA_PIN, fl::u8 CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12) > class SK9822Controller : public APA102Controller< DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED, fl::kFiveBitGammaCorrectionMode_Null, 0x00000000, 0x00000000 > { }; /// SK9822 controller class. It's exactly the same as the APA102Controller protocol but with a different END_FRAME and default SPI_SPEED. /// @tparam DATA_PIN the data pin for these LEDs /// @tparam CLOCK_PIN the clock pin for these LEDs /// @tparam RGB_ORDER the RGB ordering for these LEDs /// @tparam SPI_SPEED the clock divider used for these LEDs. Set using the ::DATA_RATE_MHZ / ::DATA_RATE_KHZ macros. Defaults to ::DATA_RATE_MHZ(24) template < fl::u8 DATA_PIN, fl::u8 CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12) > class SK9822ControllerHD : public APA102Controller< DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED, fl::kFiveBitGammaCorrectionMode_BitShift, 0x00000000, 0x00000000 > { }; /// HD107 is just the APA102 with a default 40Mhz clock rate. template < fl::u8 DATA_PIN, fl::u8 CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(40) > class HD107Controller : public APA102Controller< DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED, fl::kFiveBitGammaCorrectionMode_Null, 0x00000000, 0x00000000 > {}; /// HD107HD is just the APA102HD with a default 40Mhz clock rate. template < fl::u8 DATA_PIN, fl::u8 CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(40) > class HD107HDController : public APA102ControllerHD< DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED> { }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // P9813 definition - takes data/clock/select pin values (N.B. should take an SPI definition?) // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// P9813 controller class. /// @tparam DATA_PIN the data pin for these LEDs /// @tparam CLOCK_PIN the clock pin for these LEDs /// @tparam RGB_ORDER the RGB ordering for these LEDs /// @tparam SPI_SPEED the clock divider used for these LEDs. Set using the ::DATA_RATE_MHZ / ::DATA_RATE_KHZ macros. Defaults to ::DATA_RATE_MHZ(10) template class P9813Controller : public CPixelLEDController { typedef SPIOutput SPI; SPI mSPI; void writeBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); } FASTLED_FORCE_INLINE void writeLed(fl::u8 r, fl::u8 g, fl::u8 b) { FASTLED_REGISTER fl::u8 top = 0xC0 | ((~b & 0xC0) >> 2) | ((~g & 0xC0) >> 4) | ((~r & 0xC0) >> 6); mSPI.writeByte(top); mSPI.writeByte(b); mSPI.writeByte(g); mSPI.writeByte(r); } public: P9813Controller() {} virtual void init() { mSPI.init(); } protected: /// @copydoc CPixelLEDController::showPixels() virtual void showPixels(PixelController & pixels) { mSPI.select(); writeBoundary(); while(pixels.has(1)) { writeLed(pixels.loadAndScale0(), pixels.loadAndScale1(), pixels.loadAndScale2()); pixels.advanceData(); pixels.stepDithering(); } writeBoundary(); mSPI.waitFully(); mSPI.release(); } }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SM16716 definition - takes data/clock/select pin values (N.B. should take an SPI definition?) // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// SM16716 controller class. /// @tparam DATA_PIN the data pin for these LEDs /// @tparam CLOCK_PIN the clock pin for these LEDs /// @tparam RGB_ORDER the RGB ordering for these LEDs /// @tparam SPI_SPEED the clock divider used for these LEDs. Set using the ::DATA_RATE_MHZ / ::DATA_RATE_KHZ macros. Defaults to ::DATA_RATE_MHZ(16) template class SM16716Controller : public CPixelLEDController { typedef SPIOutput SPI; SPI mSPI; void writeHeader() { // Write out 50 zeros to the spi line (6 blocks of 8 followed by two single bit writes) mSPI.select(); mSPI.template writeBit<0>(0); mSPI.writeByte(0); mSPI.writeByte(0); mSPI.writeByte(0); mSPI.template writeBit<0>(0); mSPI.writeByte(0); mSPI.writeByte(0); mSPI.writeByte(0); mSPI.waitFully(); mSPI.release(); } public: SM16716Controller() {} virtual void init() { mSPI.init(); } protected: /// @copydoc CPixelLEDController::showPixels() virtual void showPixels(PixelController & pixels) { // Make sure the FLAG_START_BIT flag is set to ensure that an extra 1 bit is sent at the start // of each triplet of bytes for rgb data // writeHeader(); mSPI.template writePixels(pixels, NULL); writeHeader(); } }; /// @} ClockedChipsets ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Clockless template instantiations - see clockless.h for how the timing values are used // #ifdef FASTLED_HAS_CLOCKLESS /// @defgroup ClocklessChipsets Clockless Chipsets /// These chipsets have only a single data line. /// /// The clockless chipset controllers use the same base class /// and the same protocol, but with varying timing periods. /// /// These controllers have 3 control points in their cycle for each bit: /// @code /// At T=0 : the line is raised hi to start a bit /// At T=T1 : the line is dropped low to transmit a zero bit /// At T=T1+T2 : the line is dropped low to transmit a one bit /// At T=T1+T2+T3 : the cycle is concluded (next bit can be sent) /// @endcode /// /// The units used for T1, T2, and T3 is nanoseconds. /// /// For 8MHz/16MHz/24MHz frequencies, these values are also guaranteed /// to be integral multiples of an 8MHz clock (125ns increments). /// /// @note The base class, ClocklessController, is platform-specific. /// @{ // Allow clock that clockless controller is based on to have different // frequency than the CPU. #if !defined(CLOCKLESS_FREQUENCY) #define CLOCKLESS_FREQUENCY F_CPU #endif // We want to force all avr's to use the Trinket controller when running at 8Mhz, because even the 328's at 8Mhz // need the more tightly defined timeframes. #if defined(__LGT8F__) || (CLOCKLESS_FREQUENCY == 8000000 || CLOCKLESS_FREQUENCY == 16000000 || CLOCKLESS_FREQUENCY == 24000000) || defined(FASTLED_DOXYGEN) // || CLOCKLESS_FREQUENCY == 48000000 || CLOCKLESS_FREQUENCY == 96000000) // 125ns/clock /// Frequency multiplier for each clockless data interval. /// @see Notes in @ref ClocklessChipsets #define FMUL (CLOCKLESS_FREQUENCY/8000000) /// GE8822 controller class. /// @copydetails WS2812Controller800Khz template class GE8822Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// LPD1886 controller class. /// @copydetails WS2812Controller800Khz template class LPD1886Controller1250Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// LPD1886 controller class. /// @copydetails WS2812Controller800Khz template class LPD1886Controller1250Khz_8bit : public FASTLED_CLOCKLESS_CONTROLLER {}; #if !FASTLED_WS2812_HAS_SPECIAL_DRIVER /// WS2812 controller class @ 800 KHz. /// @tparam DATA_PIN the data pin for these LEDs /// @tparam RGB_ORDER the RGB ordering for these LEDs template class WS2812Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; #endif /// WS2815 controller class @ 400 KHz. /// @copydetails WS2812Controller800Khz template class WS2815Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; /// WS2811 controller class @ 800 KHz. /// @copydetails WS2812Controller800Khz template class WS2811Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// DP1903 controller class @ 800 KHz. /// @copydetails WS2812Controller800Khz template class DP1903Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// DP1903 controller class @ 400 KHz. /// @copydetails WS2812Controller800Khz template class DP1903Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// WS2813 controller class. /// @copydetails WS2812Controller800Khz template //not tested class WS2813Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; /// WS2811 controller class @ 400 KHz. /// @copydetails WS2812Controller800Khz template class WS2811Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// SK6822 controller class. /// @copydetails WS2812Controller800Khz template class SK6822Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; /// SM16703 controller class. /// @copydetails WS2812Controller800Khz template class SM16703Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; /// SK6812 controller class. /// @copydetails WS2812Controller800Khz template class SK6812Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; /// UCS1903 controller class @ 400 KHz. /// @copydetails WS2812Controller800Khz template class UCS1903Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// UCS1903B controller class. /// @copydetails WS2812Controller800Khz template class UCS1903BController800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// UCS1904 controller class. /// @copydetails WS2812Controller800Khz template class UCS1904Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// UCS2903 controller class. /// @copydetails WS2812Controller800Khz template class UCS2903Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; /// TM1809 controller class. /// @copydetails WS2812Controller800Khz template class TM1809Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// TM1803 controller class. /// @copydetails WS2812Controller800Khz template class TM1803Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// TM1829 controller class. /// @copydetails WS2812Controller800Khz template class TM1829Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// GW6205 controller class @ 400 KHz. /// @copydetails WS2812Controller800Khz template class GW6205Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// UCS1904 controller class @ 800 KHz. /// @copydetails WS2812Controller800Khz template class GW6205Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; /// PL9823 controller class. /// @copydetails WS2812Controller800Khz template class PL9823Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; // UCS1912 - Note, never been tested, this is according to the datasheet template class UCS1912Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; /// SM16824E controller class. /// @copydetails WS2812Controller800Khz template class SM16824EController : public FASTLED_CLOCKLESS_CONTROLLER {}; #else // WS2812 can be overclocked pretty aggressively, however, there are // some excellent articles that you should read about WS2812 overclocking // and corruption for a large number of LEDs. // https://wp.josh.com/2014/05/16/why-you-should-give-your-neopixel-bits-room-to-breathe/ // https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/ #ifndef FASTLED_OVERCLOCK_WS2812 #define FASTLED_OVERCLOCK_WS2812 FASTLED_OVERCLOCK #endif #ifndef FASTLED_OVERCLOCK_WS2811 #define FASTLED_OVERCLOCK_WS2811 FASTLED_OVERCLOCK #endif #ifndef FASTLED_OVERCLOCK_WS2813 #define FASTLED_OVERCLOCK_WS2813 FASTLED_OVERCLOCK #endif #ifndef FASTLED_OVERCLOCK_WS2815 #define FASTLED_OVERCLOCK_WS2815 FASTLED_OVERCLOCK #endif #ifndef FASTLED_OVERCLOCK_SK6822 #define FASTLED_OVERCLOCK_SK6822 FASTLED_OVERCLOCK #endif #ifndef FASTLED_OVERCLOCK_SK6812 #define FASTLED_OVERCLOCK_SK6812 FASTLED_OVERCLOCK #endif /// Calculates the number of cycles for the clockless chipset (which may differ from CPU cycles) /// @see ::NS() #if FASTLED_CLOCKLESS_USES_NANOSECONDS // just use raw nanosecond values for the teensy4 #define C_NS(_NS) _NS #else #define C_NS(_NS) (((_NS * ((CLOCKLESS_FREQUENCY / 1000000L)) + 999)) / 1000) #endif // Allow overclocking various LED chipsets in the clockless family. // Clocked chips like the APA102 don't need this because they allow // you to control the clock speed directly. #define C_NS_WS2812(_NS) (C_NS(int(_NS / FASTLED_OVERCLOCK_WS2812))) #define C_NS_WS2811(_NS) (C_NS(int(_NS / FASTLED_OVERCLOCK_WS2811))) #define C_NS_WS2813(_NS) (C_NS(int(_NS / FASTLED_OVERCLOCK_WS2813))) #define C_NS_WS2815(_NS) (C_NS(int(_NS / FASTLED_OVERCLOCK_WS2815))) #define C_NS_SK6822(_NS) (C_NS(int(_NS / FASTLED_OVERCLOCK_SK6822))) #define C_NS_SK6812(_NS) (C_NS(int(_NS / FASTLED_OVERCLOCK_SK6812))) // At T=0 : the line is raised hi to start a bit // At T=T1 : the line is dropped low to transmit a zero bit // At T=T1+T2 : the line is dropped low to transmit a one bit // At T=T1+T2+T3 : the cycle is concluded (next bit can be sent) // // Python script to calculate the values for T1, T2, and T3 for FastLED: // Note: there is a discussion on whether this python script is correct or not: // https://github.com/FastLED/FastLED/issues/1806 // // print("Enter the values of T0H, T0L, T1H, T1L, in nanoseconds: ") // T0H = int(input(" T0H: ")) // T0L = int(input(" T0L: ")) // T1H = int(input(" T1H: ")) // T1L = int(input(" T1L: ")) // // duration = max(T0H + T0L, T1H + T1L) // // print("The max duration of the signal is: ", duration) // // T1 = T0H // T2 = T1H // T3 = duration - T0H - T0L // // print("T1: ", T1) // print("T2: ", T2) // print("T3: ", T3) // GE8822 - 350ns 660ns 350ns template class GE8822Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; // GW6205@400khz - 800ns, 800ns, 800ns template class GW6205Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; // GW6205@400khz - 400ns, 400ns, 400ns template class GW6205Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; // UCS1903 - 500ns, 1500ns, 500ns template class UCS1903Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; // UCS1903B - 400ns, 450ns, 450ns template class UCS1903BController800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; // UCS1904 - 400ns, 400ns, 450ns template class UCS1904Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; // UCS2903 - 250ns, 750ns, 250ns template class UCS2903Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; // TM1809 - 350ns, 350ns, 550ns template class TM1809Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; // WS2811 - 320ns, 320ns, 640ns template class WS2811Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; // WS2813 - 320ns, 320ns, 640ns template class WS2813Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; #ifndef FASTLED_WS2812_T1 #define FASTLED_WS2812_T1 250 #endif #ifndef FASTLED_WS2812_T2 #define FASTLED_WS2812_T2 625 #endif #ifndef FASTLED_WS2812_T3 #define FASTLED_WS2812_T3 375 #endif #if !FASTLED_WS2812_HAS_SPECIAL_DRIVER template class WS2812Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER< DATA_PIN, C_NS_WS2812(FASTLED_WS2812_T1), C_NS_WS2812(FASTLED_WS2812_T2), C_NS_WS2812(FASTLED_WS2812_T3), RGB_ORDER> {}; #endif // WS2811@400khz - 800ns, 800ns, 900ns template class WS2811Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; template class WS2815Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; // 750NS, 750NS, 750NS template class TM1803Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; template class TM1829Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; template class TM1829Controller1600Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; template class LPD1886Controller1250Khz : public FASTLED_CLOCKLESS_CONTROLLER {}; template class LPD1886Controller1250Khz_8bit : public FASTLED_CLOCKLESS_CONTROLLER {}; template class SK6822Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; template class SK6812Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; template class SM16703Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; template class PL9823Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; // UCS1912 - Note, never been tested, this is according to the datasheet template class UCS1912Controller : public FASTLED_CLOCKLESS_CONTROLLER {}; // NEW LED! Help us test it! // Under developement. // SM16824E - 300ns, 900ns, 0ns // * T0H: .3 // * T0L: .9 // * T1H: .9 // * T1L: .3 // * TRST: 200 template class SM16824EController : public FASTLED_CLOCKLESS_CONTROLLER {}; #endif /// @} ClocklessChipsets // WS2816 - is an emulated controller that emits 48 bit pixels by forwarding // them to a platform specific WS2812 controller. The WS2812 controller // has to output twice as many 24 bit pixels. template class WS2816Controller : public CPixelLEDController::LANES_VALUE, WS2812Controller800Khz::MASK_VALUE> { public: // ControllerT is a helper class. It subclasses the device controller class // and has three methods to call the three protected methods we use. // This is janky, but redeclaring public methods protected in a derived class // is janky, too. // N.B., byte order must be RGB. typedef WS2812Controller800Khz ControllerBaseT; class ControllerT : public ControllerBaseT { friend class WS2816Controller; void *callBeginShowLeds(int size) { return ControllerBaseT::beginShowLeds(size); } void callShow(CRGB *data, int nLeds, fl::u8 brightness) { ControllerBaseT::show(data, nLeds, brightness); } void callEndShowLeds(void *data) { ControllerBaseT::endShowLeds(data); } }; static const int LANES = ControllerT::LANES_VALUE; static const uint32_t MASK = ControllerT::MASK_VALUE; WS2816Controller() {} ~WS2816Controller() { mController.setLeds(nullptr, 0); delete [] mData; } virtual void *beginShowLeds(int size) override { mController.setEnabled(true); void *result = mController.callBeginShowLeds(2 * size); mController.setEnabled(false); return result; } virtual void endShowLeds(void *data) override { mController.setEnabled(true); mController.callEndShowLeds(data); mController.setEnabled(false); } virtual void showPixels(PixelController &pixels) override { // Ensure buffer is large enough ensureBuffer(pixels.size()); // expand and copy all the pixels size_t out_index = 0; while (pixels.has(1)) { pixels.stepDithering(); fl::u16 s0, s1, s2; pixels.loadAndScale_WS2816_HD(&s0, &s1, &s2); fl::u8 b0_hi = s0 >> 8; fl::u8 b0_lo = s0 & 0xFF; fl::u8 b1_hi = s1 >> 8; fl::u8 b1_lo = s1 & 0xFF; fl::u8 b2_hi = s2 >> 8; fl::u8 b2_lo = s2 & 0xFF; mData[out_index] = CRGB(b0_hi, b0_lo, b1_hi); mData[out_index + 1] = CRGB(b1_lo, b2_hi, b2_lo); pixels.advanceData(); out_index += 2; } // ensure device controller won't modify color values mController.setCorrection(CRGB(255, 255, 255)); mController.setTemperature(CRGB(255, 255, 255)); mController.setDither(DISABLE_DITHER); // output the data stream mController.setEnabled(true); #ifdef BOUNCE_SUBCLASS mController.callShow(mData, 2 * pixels.size(), 255); #else mController.show(mData, 2 * pixels.size(), 255); #endif mController.setEnabled(false); } private: void init() override { mController.init(); mController.setEnabled(false); } void ensureBuffer(int size_8bit) { int size_16bit = 2 * size_8bit; if (mController.size() != size_16bit) { delete [] mData; CRGB *new_leds = new CRGB[size_16bit]; mData = new_leds; mController.setLeds(new_leds, size_16bit); } } CRGB *mData = 0; ControllerT mController; }; #endif /// @} Chipsets FASTLED_NAMESPACE_END #endif