Files
klubhaus-doorbell/libraries/FastLED/src/chipsets.h
2026-02-12 00:45:31 -08:00

1242 lines
44 KiB
C++

#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 <SoftwareSerial.h>
#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<fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class PixieController : public CPixelLEDController<RGB_ORDER> {
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<RGB_ORDER> & 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<SoftwareSerial & STREAM, EOrder RGB_ORDER = RGB>
// class PixieController : public PixieBaseController<STREAM, RGB_ORDER> {
// 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<RGB_ORDER, CONTROLLER::LANES_VALUE,
CONTROLLER::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 CONTROLLER ControllerBaseT;
class ControllerT : public CONTROLLER {
friend class RGBWEmulatedController<CONTROLLER, RGB_ORDER>;
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<RGB_ORDER, LANES, MASK> &pixels) override {
// Ensure buffer is large enough
ensureBuffer(pixels.size());
Rgbw rgbw = this->getRgbw();
fl::u8 *data = fl::bit_cast_ptr<fl::u8>(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 <fl::u8 DATA_PIN, fl::u8 CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12) >
class LPD8806Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> 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<SPI*>(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<RGB_ORDER> & 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 <fl::u8 DATA_PIN, fl::u8 CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(1)>
class WS2801Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> 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<RGB_ORDER> & pixels) {
mWaitDelay.wait();
mSPI.template writePixels<0, DATA_NOP, RGB_ORDER>(pixels, NULL);
mWaitDelay.mark();
}
};
/// WS2803 controller class.
/// @copydetails WS2801Controller
template <fl::u8 DATA_PIN, fl::u8 CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(25)>
class WS2803Controller : public WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED> {};
/// 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 <fl::u8 DATA_PIN, fl::u8 CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12)>
class LPD6803Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> 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<RGB_ORDER> & 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<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> 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<RGB_ORDER> & 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<RGB_ORDER> & 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<fl::u8>(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<fl::u8>(brightness);
#endif // FASTLED_HD_COLOR_MIXING
}
// Legacy showPixels implementation.
inline void showPixelsDefault(PixelController<RGB_ORDER> & 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<RGB_ORDER> & 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 <fl::u8 DATA_PIN, fl::u8 CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(10)>
class P9813Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> 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<RGB_ORDER> & 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 <fl::u8 DATA_PIN, fl::u8 CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(16)>
class SM16716Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> 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<RGB_ORDER> & 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<FLAG_START_BIT, DATA_NOP, RGB_ORDER>(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 <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class GE8822Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 3 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER, 4> {};
/// LPD1886 controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class LPD1886Controller1250Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 2 * FMUL, 3 * FMUL, 2 * FMUL, RGB_ORDER, 4> {};
/// LPD1886 controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class LPD1886Controller1250Khz_8bit : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 2 * FMUL, 3 * FMUL, 2 * FMUL, RGB_ORDER> {};
#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 <fl::u8 DATA_PIN, EOrder RGB_ORDER = GRB>
class WS2812Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER> {};
#endif
/// WS2815 controller class @ 400 KHz.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = GRB>
class WS2815Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 2 * FMUL, 9 * FMUL, 4 * FMUL, RGB_ORDER> {};
/// WS2811 controller class @ 800 KHz.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = GRB>
class WS2811Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 3 * FMUL, 4 * FMUL, 3 * FMUL, RGB_ORDER> {};
/// DP1903 controller class @ 800 KHz.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class DP1903Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 2 * FMUL, 8 * FMUL, 2 * FMUL, RGB_ORDER> {};
/// DP1903 controller class @ 400 KHz.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class DP1903Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 4 * FMUL, 16 * FMUL, 4 * FMUL, RGB_ORDER> {};
/// WS2813 controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = GRB> //not tested
class WS2813Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 3 * FMUL, 4 * FMUL, 3 * FMUL, RGB_ORDER> {};
/// WS2811 controller class @ 400 KHz.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = GRB>
class WS2811Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 4 * FMUL, 10 * FMUL, 6 * FMUL, RGB_ORDER> {};
/// SK6822 controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class SK6822Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 3 * FMUL, 8 * FMUL, 3 * FMUL, RGB_ORDER> {};
/// SM16703 controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class SM16703Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 3 * FMUL, 4 * FMUL, 3 * FMUL, RGB_ORDER> {};
/// SK6812 controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class SK6812Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 3 * FMUL, 3 * FMUL, 4 * FMUL, RGB_ORDER> {};
/// UCS1903 controller class @ 400 KHz.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 4 * FMUL, 12 * FMUL, 4 * FMUL, RGB_ORDER> {};
/// UCS1903B controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903BController800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 2 * FMUL, 4 * FMUL, 4 * FMUL, RGB_ORDER> {};
/// UCS1904 controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1904Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 3 * FMUL, 3 * FMUL, 4 * FMUL, RGB_ORDER> {};
/// UCS2903 controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS2903Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 2 * FMUL, 6 * FMUL, 2 * FMUL, RGB_ORDER> {};
/// TM1809 controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1809Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER> {};
/// TM1803 controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1803Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 6 * FMUL, 9 * FMUL, 6 * FMUL, RGB_ORDER> {};
/// TM1829 controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1829Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER, 0, true, 500> {};
/// GW6205 controller class @ 400 KHz.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class GW6205Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 6 * FMUL, 7 * FMUL, 6 * FMUL, RGB_ORDER, 4> {};
/// UCS1904 controller class @ 800 KHz.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class GW6205Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 2 * FMUL, 4 * FMUL, 4 * FMUL, RGB_ORDER, 4> {};
/// PL9823 controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class PL9823Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 3 * FMUL, 8 * FMUL, 3 * FMUL, RGB_ORDER> {};
// UCS1912 - Note, never been tested, this is according to the datasheet
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1912Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 2 * FMUL, 8 * FMUL, 3 * FMUL, RGB_ORDER> {};
/// SM16824E controller class.
/// @copydetails WS2812Controller800Khz
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class SM16824EController : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, 3 * FMUL, 9 * FMUL, 1 * FMUL, RGB_ORDER, 0, false, 200> {};
#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 <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class GE8822Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(350), C_NS(660), C_NS(350), RGB_ORDER, 4> {};
// GW6205@400khz - 800ns, 800ns, 800ns
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class GW6205Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(800), C_NS(800), C_NS(800), RGB_ORDER, 4> {};
// GW6205@400khz - 400ns, 400ns, 400ns
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class GW6205Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(400), C_NS(400), C_NS(400), RGB_ORDER, 4> {};
// UCS1903 - 500ns, 1500ns, 500ns
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(500), C_NS(1500), C_NS(500), RGB_ORDER> {};
// UCS1903B - 400ns, 450ns, 450ns
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903BController800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(400), C_NS(450), C_NS(450), RGB_ORDER> {};
// UCS1904 - 400ns, 400ns, 450ns
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1904Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(400), C_NS(400), C_NS(450), RGB_ORDER> {};
// UCS2903 - 250ns, 750ns, 250ns
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS2903Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(250), C_NS(750), C_NS(250), RGB_ORDER> {};
// TM1809 - 350ns, 350ns, 550ns
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1809Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(350), C_NS(350), C_NS(450), RGB_ORDER> {};
// WS2811 - 320ns, 320ns, 640ns
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = GRB>
class WS2811Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS_WS2811(320), C_NS_WS2811(320), C_NS_WS2811(640), RGB_ORDER> {};
// WS2813 - 320ns, 320ns, 640ns
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = GRB>
class WS2813Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS_WS2813(320), C_NS_WS2813(320), C_NS_WS2813(640), RGB_ORDER> {};
#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 <fl::u8 DATA_PIN, EOrder RGB_ORDER = GRB>
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 <fl::u8 DATA_PIN, EOrder RGB_ORDER = GRB>
class WS2811Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS_WS2811(800), C_NS_WS2811(800), C_NS_WS2811(900), RGB_ORDER> {};
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = GRB>
class WS2815Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS_WS2815(250), C_NS_WS2815(1090), C_NS_WS2815(550), RGB_ORDER> {};
// 750NS, 750NS, 750NS
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1803Controller400Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(700), C_NS(1100), C_NS(700), RGB_ORDER> {};
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1829Controller800Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(340), C_NS(340), C_NS(550), RGB_ORDER, 0, true, 500> {};
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1829Controller1600Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(100), C_NS(300), C_NS(200), RGB_ORDER, 0, true, 500> {};
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class LPD1886Controller1250Khz : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(200), C_NS(400), C_NS(200), RGB_ORDER, 4> {};
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class LPD1886Controller1250Khz_8bit : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(200), C_NS(400), C_NS(200), RGB_ORDER> {};
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class SK6822Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS_SK6822(375), C_NS_SK6822(1000), C_NS_SK6822(375), RGB_ORDER> {};
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class SK6812Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS_SK6812(300), C_NS_SK6812(300), C_NS_SK6812(600), RGB_ORDER> {};
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class SM16703Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(300), C_NS(600), C_NS(300), RGB_ORDER> {};
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class PL9823Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(350), C_NS(1010), C_NS(350), RGB_ORDER> {};
// UCS1912 - Note, never been tested, this is according to the datasheet
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1912Controller : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(250), C_NS(1000), C_NS(350), RGB_ORDER> {};
// NEW LED! Help us test it!
// Under developement.
// SM16824E - 300ns, 900ns, 0ns
// * T0H: .3
// * T0L: .9
// * T1H: .9
// * T1L: .3
// * TRST: 200
template <fl::u8 DATA_PIN, EOrder RGB_ORDER = RGB>
class SM16824EController : public FASTLED_CLOCKLESS_CONTROLLER<DATA_PIN, C_NS(300), C_NS(900), C_NS(100), RGB_ORDER, 0, false, 200> {};
#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 <fl::u8 DATA_PIN, EOrder RGB_ORDER = GRB>
class WS2816Controller
: public CPixelLEDController<RGB_ORDER,
WS2812Controller800Khz<DATA_PIN, RGB>::LANES_VALUE,
WS2812Controller800Khz<DATA_PIN, RGB>::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<DATA_PIN, RGB> ControllerBaseT;
class ControllerT : public ControllerBaseT {
friend class WS2816Controller<DATA_PIN, RGB_ORDER>;
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<RGB_ORDER, LANES, MASK> &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