Implements 5bit hd gamma correction using the bit shift method for APA102 and SK9822 chipsets
This commit is contained in:
parent
3d2ab780b4
commit
a36b331703
@ -17,7 +17,7 @@
|
|||||||
set -eou pipefail
|
set -eou pipefail
|
||||||
|
|
||||||
# List of examples that will be compiled by default
|
# List of examples that will be compiled by default
|
||||||
EXAMPLES=${EXAMPLES:-"Blink ColorPalette ColorTemperature Cylon DemoReel100
|
EXAMPLES=${EXAMPLES:-"Apa102HD Blink ColorPalette ColorTemperature Cylon DemoReel100
|
||||||
Fire2012 FirstLight Multiple/MultipleStripsInOneArray
|
Fire2012 FirstLight Multiple/MultipleStripsInOneArray
|
||||||
Multiple/ArrayOfLedArrays Noise NoisePlayground NoisePlusPalette Pacifica
|
Multiple/ArrayOfLedArrays Noise NoisePlayground NoisePlusPalette Pacifica
|
||||||
Pride2015 RGBCalibrate RGBSetDemo TwinkleFox XYMatrix"}
|
Pride2015 RGBCalibrate RGBSetDemo TwinkleFox XYMatrix"}
|
||||||
|
47
examples/Apa102HD/Apa102HD.ino
Normal file
47
examples/Apa102HD/Apa102HD.ino
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/// @file Apa102HD.ino
|
||||||
|
/// @brief Example showing how to use the APA102HD gamma correction.
|
||||||
|
/// @example Apa102HD.ino
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <FastLED.h>
|
||||||
|
|
||||||
|
#define NUM_LEDS 20
|
||||||
|
|
||||||
|
static bool gamma_function_hit = false;
|
||||||
|
CRGB leds_hd[NUM_LEDS] = {0}; // HD mode implies gamma.
|
||||||
|
CRGB leds[NUM_LEDS] = {0}; // Software gamma mode.
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t gamma8(uint8_t x) {
|
||||||
|
return uint8_t((uint16_t(x) * x) >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
CRGB gammaCorrect(CRGB c) {
|
||||||
|
c.r = gamma8(c.r);
|
||||||
|
c.g = gamma8(c.g);
|
||||||
|
c.b = gamma8(c.b);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
delay(500); // power-up safety delay
|
||||||
|
FastLED.addLeds<APA102HD, 1, 2, RGB>(leds_hd, NUM_LEDS);
|
||||||
|
FastLED.addLeds<APA102, 3, 4, RGB>(leds, NUM_LEDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
uint32_t now = millis();
|
||||||
|
uint32_t t = now / 100;
|
||||||
|
for (int i = 0; i < NUM_LEDS; i++) {
|
||||||
|
uint8_t brightness = sin8(t + i);
|
||||||
|
CRGB c(brightness, brightness, brightness);
|
||||||
|
leds_hd[i] = c;
|
||||||
|
leds[i] = gammaCorrect(c);
|
||||||
|
}
|
||||||
|
FastLED.show();
|
||||||
|
delay(8);
|
||||||
|
if (!gamma_function_hit) {
|
||||||
|
Serial.println("gamma function not hit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -87,7 +87,9 @@ enum ESPIChipsets {
|
|||||||
P9813, ///< P9813 LED chipset
|
P9813, ///< P9813 LED chipset
|
||||||
APA102, ///< APA102 LED chipset
|
APA102, ///< APA102 LED chipset
|
||||||
SK9822, ///< SK9822 LED chipset
|
SK9822, ///< SK9822 LED chipset
|
||||||
DOTSTAR ///< APA102 LED chipset alias
|
SK9822HD, ///< SK9822 LED chipset with 5-bit gamma correction
|
||||||
|
DOTSTAR, ///< APA102 LED chipset alias
|
||||||
|
APA102HD, ///< APA102 LED chipset with 5-bit gamma correction
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Smart Matrix Library controller type
|
/// Smart Matrix Library controller type
|
||||||
@ -274,7 +276,9 @@ public:
|
|||||||
case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
case DOTSTAR:
|
case DOTSTAR:
|
||||||
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
|
case APA102HD: { static APA102ControllerHD<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
|
case SK9822HD: { static SK9822ControllerHD<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +293,9 @@ public:
|
|||||||
case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
case DOTSTAR:
|
case DOTSTAR:
|
||||||
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
|
case APA102HD: { static APA102ControllerHD<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
|
case SK9822HD: { static SK9822ControllerHD<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,7 +310,9 @@ public:
|
|||||||
case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
case DOTSTAR:
|
case DOTSTAR:
|
||||||
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
|
case APA102HD: { static APA102ControllerHD<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
|
case SK9822HD: { static SK9822ControllerHD<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
216
src/chipsets.h
216
src/chipsets.h
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "FastLED.h"
|
#include "FastLED.h"
|
||||||
#include "pixeltypes.h"
|
#include "pixeltypes.h"
|
||||||
|
#include "five_bit_hd_gamma.h"
|
||||||
|
|
||||||
/// @file chipsets.h
|
/// @file chipsets.h
|
||||||
/// Contains the bulk of the definitions for the various LED chipsets supported.
|
/// Contains the bulk of the definitions for the various LED chipsets supported.
|
||||||
@ -214,13 +215,35 @@ protected:
|
|||||||
/// @tparam CLOCK_PIN the clock pin for these LEDs
|
/// @tparam CLOCK_PIN the clock pin for these LEDs
|
||||||
/// @tparam RGB_ORDER the RGB ordering 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)
|
/// @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 <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12)>
|
template <
|
||||||
|
uint8_t DATA_PIN, uint8_t CLOCK_PIN,
|
||||||
|
EOrder RGB_ORDER = RGB,
|
||||||
|
uint32_t SPI_SPEED = DATA_RATE_MHZ(12),
|
||||||
|
FiveBitGammaCorrectionMode GAMMA_CORRECTION_MODE = kFiveBitGammaCorrectionMode_Null,
|
||||||
|
uint32_t START_FRAME = 0x00000000,
|
||||||
|
uint32_t END_FRAME = 0xFF000000
|
||||||
|
>
|
||||||
class APA102Controller : public CPixelLEDController<RGB_ORDER> {
|
class APA102Controller : public CPixelLEDController<RGB_ORDER> {
|
||||||
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
|
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
|
||||||
SPI mSPI;
|
SPI mSPI;
|
||||||
|
|
||||||
void startBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); }
|
void startBoundary() {
|
||||||
void endBoundary(int nLeds) { int nDWords = (nLeds/32); do { mSPI.writeByte(0xFF); mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); } while(nDWords--); }
|
mSPI.writeWord(START_FRAME >> 16);
|
||||||
|
mSPI.writeWord(START_FRAME & 0xFFFF);
|
||||||
|
}
|
||||||
|
void endBoundary(int nLeds) {
|
||||||
|
int nDWords = (nLeds/32);
|
||||||
|
const uint8_t b0 = uint8_t(END_FRAME >> 24 & 0x000000ff);
|
||||||
|
const uint8_t b1 = uint8_t(END_FRAME >> 16 & 0x000000ff);
|
||||||
|
const uint8_t b2 = uint8_t(END_FRAME >> 8 & 0x000000ff);
|
||||||
|
const uint8_t b3 = uint8_t(END_FRAME >> 0 & 0x000000ff);
|
||||||
|
do {
|
||||||
|
mSPI.writeByte(b0);
|
||||||
|
mSPI.writeByte(b1);
|
||||||
|
mSPI.writeByte(b2);
|
||||||
|
mSPI.writeByte(b3);
|
||||||
|
} while(nDWords--);
|
||||||
|
}
|
||||||
|
|
||||||
inline void writeLed(uint8_t brightness, uint8_t b0, uint8_t b1, uint8_t b2) __attribute__((always_inline)) {
|
inline void writeLed(uint8_t brightness, uint8_t b0, uint8_t b1, uint8_t b2) __attribute__((always_inline)) {
|
||||||
#ifdef FASTLED_SPI_BYTE_ONLY
|
#ifdef FASTLED_SPI_BYTE_ONLY
|
||||||
@ -237,6 +260,15 @@ class APA102Controller : public CPixelLEDController<RGB_ORDER> {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void write2Bytes(uint8_t b1, uint8_t b2) __attribute__((always_inline)) {
|
||||||
|
#ifdef FASTLED_SPI_BYTE_ONLY
|
||||||
|
mSPI.writeByte(b1);
|
||||||
|
mSPI.writeByte(b2);
|
||||||
|
#else
|
||||||
|
mSPI.writeWord(uint16_t(b1) << 8 | b2);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
APA102Controller() {}
|
APA102Controller() {}
|
||||||
|
|
||||||
@ -247,9 +279,26 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
/// @copydoc CPixelLEDController::showPixels()
|
/// @copydoc CPixelLEDController::showPixels()
|
||||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||||
mSPI.select();
|
switch (GAMMA_CORRECTION_MODE) {
|
||||||
|
case kFiveBitGammaCorrectionMode_Null: {
|
||||||
|
showPixelsDefault(pixels);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kFiveBitGammaCorrectionMode_BitShift: {
|
||||||
|
showPixelsGammaBitShift(pixels);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t s0 = pixels.getScale0(), s1 = pixels.getScale1(), s2 = pixels.getScale2();
|
private:
|
||||||
|
|
||||||
|
static inline void getGlobalBrightnessAndScalingFactors(
|
||||||
|
PixelController<RGB_ORDER>& pixels,
|
||||||
|
uint8_t* out_s0, uint8_t* out_s1, uint8_t* out_s2, uint8_t* out_brightness) {
|
||||||
|
uint8_t s0 = pixels.getScale0();
|
||||||
|
uint8_t s1 = pixels.getScale1();
|
||||||
|
uint8_t s2 = pixels.getScale2();
|
||||||
#if FASTLED_USE_GLOBAL_BRIGHTNESS == 1
|
#if FASTLED_USE_GLOBAL_BRIGHTNESS == 1
|
||||||
const uint16_t maxBrightness = 0x1F;
|
const uint16_t maxBrightness = 0x1F;
|
||||||
uint16_t brightness = ((((uint16_t)max(max(s0, s1), s2) + 1) * maxBrightness - 1) >> 8) + 1;
|
uint16_t brightness = ((((uint16_t)max(max(s0, s1), s2) + 1) * maxBrightness - 1) >> 8) + 1;
|
||||||
@ -259,10 +308,23 @@ protected:
|
|||||||
#else
|
#else
|
||||||
const uint8_t brightness = 0x1F;
|
const uint8_t brightness = 0x1F;
|
||||||
#endif
|
#endif
|
||||||
|
*out_s0 = s0;
|
||||||
|
*out_s1 = s1;
|
||||||
|
*out_s2 = s2;
|
||||||
|
*out_brightness = static_cast<uint8_t>(brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy showPixels implementation.
|
||||||
|
inline void showPixelsDefault(PixelController<RGB_ORDER> & pixels) {
|
||||||
|
mSPI.select();
|
||||||
|
uint8_t s0, s1, s2, global_brightness;
|
||||||
|
getGlobalBrightnessAndScalingFactors(pixels, &s0, &s1, &s2, &global_brightness);
|
||||||
startBoundary();
|
startBoundary();
|
||||||
while (pixels.has(1)) {
|
while (pixels.has(1)) {
|
||||||
writeLed(brightness, pixels.loadAndScale0(0, s0), pixels.loadAndScale1(0, s1), pixels.loadAndScale2(0, s2));
|
uint8_t r = pixels.loadAndScale0(0, s0);
|
||||||
|
uint8_t g = pixels.loadAndScale1(0, s1);
|
||||||
|
uint8_t b = pixels.loadAndScale2(0, s2);
|
||||||
|
writeLed(global_brightness, r, g, b);
|
||||||
pixels.stepDithering();
|
pixels.stepDithering();
|
||||||
pixels.advanceData();
|
pixels.advanceData();
|
||||||
}
|
}
|
||||||
@ -272,72 +334,96 @@ protected:
|
|||||||
mSPI.release();
|
mSPI.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void showPixelsGammaBitShift(PixelController<RGB_ORDER> & pixels) {
|
||||||
|
mSPI.select();
|
||||||
|
uint8_t s0, s1, s2, global_brightness;
|
||||||
|
getGlobalBrightnessAndScalingFactors(pixels, &s0, &s1, &s2, &global_brightness);
|
||||||
|
startBoundary();
|
||||||
|
while (pixels.has(1)) {
|
||||||
|
uint8_t r = pixels.loadAndScale0(0, s0);
|
||||||
|
uint8_t g = pixels.loadAndScale1(0, s1);
|
||||||
|
uint8_t b = pixels.loadAndScale2(0, s2);
|
||||||
|
uint8_t brightness = 0;
|
||||||
|
five_bit_hd_gamma_bitshift(r, g, b, &r, &g, &b, &brightness);
|
||||||
|
if (global_brightness >= 0x1F) {
|
||||||
|
// 5-bit mix.
|
||||||
|
brightness = static_cast<uint8_t>(
|
||||||
|
(uint16_t(brightness) * global_brightness)
|
||||||
|
/ 0x1F
|
||||||
|
);
|
||||||
|
}
|
||||||
|
writeLed(brightness, r, g, b);
|
||||||
|
pixels.stepDithering();
|
||||||
|
pixels.advanceData();
|
||||||
|
}
|
||||||
|
endBoundary(pixels.size());
|
||||||
|
|
||||||
|
mSPI.waitFully();
|
||||||
|
mSPI.release();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// SK9822 controller class.
|
template <
|
||||||
|
uint8_t DATA_PIN,
|
||||||
|
uint8_t CLOCK_PIN,
|
||||||
|
EOrder RGB_ORDER = RGB,
|
||||||
|
uint32_t SPI_SPEED = DATA_RATE_MHZ(24)
|
||||||
|
>
|
||||||
|
class APA102ControllerHD : public APA102Controller<
|
||||||
|
DATA_PIN,
|
||||||
|
CLOCK_PIN,
|
||||||
|
RGB_ORDER,
|
||||||
|
SPI_SPEED,
|
||||||
|
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 DATA_PIN the data pin for these LEDs
|
||||||
/// @tparam CLOCK_PIN the clock pin for these LEDs
|
/// @tparam CLOCK_PIN the clock pin for these LEDs
|
||||||
/// @tparam RGB_ORDER the RGB ordering 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)
|
/// @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 <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(24)>
|
template <
|
||||||
class SK9822Controller : public CPixelLEDController<RGB_ORDER> {
|
uint8_t DATA_PIN,
|
||||||
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
|
uint8_t CLOCK_PIN,
|
||||||
SPI mSPI;
|
EOrder RGB_ORDER = RGB,
|
||||||
|
uint32_t SPI_SPEED = DATA_RATE_MHZ(24)
|
||||||
void startBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); }
|
>
|
||||||
void endBoundary(int nLeds) { int nLongWords = (nLeds/32); do { mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); } while(nLongWords--); }
|
class SK9822Controller : public APA102Controller<
|
||||||
|
DATA_PIN,
|
||||||
inline void writeLed(uint8_t brightness, uint8_t b0, uint8_t b1, uint8_t b2) __attribute__((always_inline)) {
|
CLOCK_PIN,
|
||||||
#ifdef FASTLED_SPI_BYTE_ONLY
|
RGB_ORDER,
|
||||||
mSPI.writeByte(0xE0 | brightness);
|
SPI_SPEED,
|
||||||
mSPI.writeByte(b0);
|
kFiveBitGammaCorrectionMode_Null,
|
||||||
mSPI.writeByte(b1);
|
0x00000000,
|
||||||
mSPI.writeByte(b2);
|
0x00000000
|
||||||
#else
|
> {
|
||||||
uint16_t b = 0xE000 | (brightness << 8) | (uint16_t)b0;
|
};
|
||||||
mSPI.writeWord(b);
|
|
||||||
uint16_t w = b1 << 8;
|
|
||||||
w |= b2;
|
|
||||||
mSPI.writeWord(w);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
SK9822Controller() {}
|
|
||||||
|
|
||||||
virtual void init() {
|
|
||||||
mSPI.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/// @copydoc CPixelLEDController::showPixels()
|
|
||||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
|
||||||
mSPI.select();
|
|
||||||
|
|
||||||
uint8_t s0 = pixels.getScale0(), s1 = pixels.getScale1(), s2 = pixels.getScale2();
|
|
||||||
#if FASTLED_USE_GLOBAL_BRIGHTNESS == 1
|
|
||||||
const uint16_t maxBrightness = 0x1F;
|
|
||||||
uint16_t brightness = ((((uint16_t)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 uint8_t brightness = 0x1F;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
startBoundary();
|
|
||||||
while (pixels.has(1)) {
|
|
||||||
writeLed(brightness, pixels.loadAndScale0(0, s0), pixels.loadAndScale1(0, s1), pixels.loadAndScale2(0, s2));
|
|
||||||
pixels.stepDithering();
|
|
||||||
pixels.advanceData();
|
|
||||||
}
|
|
||||||
|
|
||||||
endBoundary(pixels.size());
|
|
||||||
|
|
||||||
mSPI.waitFully();
|
|
||||||
mSPI.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// 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 <
|
||||||
|
uint8_t DATA_PIN,
|
||||||
|
uint8_t CLOCK_PIN,
|
||||||
|
EOrder RGB_ORDER = RGB,
|
||||||
|
uint32_t SPI_SPEED = DATA_RATE_MHZ(24)
|
||||||
|
>
|
||||||
|
class SK9822ControllerHD : public APA102Controller<
|
||||||
|
DATA_PIN,
|
||||||
|
CLOCK_PIN,
|
||||||
|
RGB_ORDER,
|
||||||
|
SPI_SPEED,
|
||||||
|
kFiveBitGammaCorrectionMode_BitShift,
|
||||||
|
0x00000000,
|
||||||
|
0x00000000
|
||||||
|
> {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
162
src/five_bit_hd_gamma.cpp
Normal file
162
src/five_bit_hd_gamma.cpp
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
#include "FastLED.h"
|
||||||
|
#include "five_bit_hd_gamma.h"
|
||||||
|
|
||||||
|
FASTLED_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((weak))
|
||||||
|
void five_bit_hd_gamma_function(
|
||||||
|
uint8_t r8, uint8_t g8, uint8_t b8,
|
||||||
|
uint16_t* r16, uint16_t* g16, uint16_t* b16) {
|
||||||
|
*r16 = uint16_t(r8) * r8;
|
||||||
|
*g16 = uint16_t(g8) * g8;
|
||||||
|
*b16 = uint16_t(b8) * b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak))
|
||||||
|
void five_bit_hd_gamma_bitshift(
|
||||||
|
uint8_t r8, uint8_t g8, uint8_t b8,
|
||||||
|
uint8_t* out_r8,
|
||||||
|
uint8_t* out_g8,
|
||||||
|
uint8_t* out_b8,
|
||||||
|
uint8_t* out_power_5bit) {
|
||||||
|
|
||||||
|
// Step 1: Gamma Correction
|
||||||
|
uint16_t r16, g16, b16;
|
||||||
|
five_bit_hd_gamma_function(r8, g8, b8, &r16, &g16, &b16);
|
||||||
|
|
||||||
|
// Step 2: Initialize 5-bit brightness.
|
||||||
|
// Note: we only get 5 levels of brightness
|
||||||
|
uint8_t v8 = 31;
|
||||||
|
|
||||||
|
uint16_t nominator = 1;
|
||||||
|
uint16_t denominator = 1;
|
||||||
|
const uint16_t r16_const = r16;
|
||||||
|
const uint16_t g16_const = g16;
|
||||||
|
const uint16_t b16_const = b16;
|
||||||
|
|
||||||
|
// Step 3: Bit Shifting Loop, can probably replaced with a
|
||||||
|
// single pass bit-twiddling hack.
|
||||||
|
do {
|
||||||
|
{
|
||||||
|
uint32_t next_r16 = r16 * 31 / 15;
|
||||||
|
uint32_t next_g16 = g16 * 31 / 15;
|
||||||
|
uint32_t next_b16 = b16 * 31 / 15;
|
||||||
|
if (next_r16 > 0xffff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next_g16 > 0xffff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next_b16 > 0xffff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nominator = nominator * 31;
|
||||||
|
denominator = denominator * 15;
|
||||||
|
v8 = v8 >> 1;
|
||||||
|
r16 = next_r16;
|
||||||
|
g16 = next_g16;
|
||||||
|
b16 = next_b16;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint32_t next_r16 = r16 * 15 / 7;
|
||||||
|
uint32_t next_g16 = g16 * 15 / 7;
|
||||||
|
uint32_t next_b16 = b16 * 15 / 7;
|
||||||
|
if (next_r16 > 0xffff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next_g16 > 0xffff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next_b16 > 0xffff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nominator = nominator * 15;
|
||||||
|
denominator = denominator * 7;
|
||||||
|
v8 = v8 >> 1;
|
||||||
|
r16 = next_r16;
|
||||||
|
g16 = next_g16;
|
||||||
|
b16 = next_b16;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint32_t next_r16 = r16 * 7 / 3;
|
||||||
|
uint32_t next_g16 = g16 * 7 / 3;
|
||||||
|
uint32_t next_b16 = b16 * 7 / 3;
|
||||||
|
if (next_r16 > 0xffff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next_g16 > 0xffff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next_b16 > 0xffff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nominator = nominator * 7;
|
||||||
|
denominator = denominator * 3;
|
||||||
|
v8 = v8 >> 1;
|
||||||
|
r16 = next_r16;
|
||||||
|
g16 = next_g16;
|
||||||
|
b16 = next_b16;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint32_t next_r16 = r16 * 3;
|
||||||
|
uint32_t next_g16 = g16 * 3;
|
||||||
|
uint32_t next_b16 = b16 * 3;
|
||||||
|
if (next_r16 > 0xffff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next_g16 > 0xffff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next_b16 > 0xffff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nominator = nominator * 3;
|
||||||
|
v8 = v8 >> 1;
|
||||||
|
r16 = next_r16;
|
||||||
|
g16 = next_g16;
|
||||||
|
b16 = next_b16;
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
r16 = r16_const * nominator / denominator;
|
||||||
|
g16 = g16_const * nominator / denominator;
|
||||||
|
b16 = b16_const * nominator / denominator;
|
||||||
|
// protect against overflow
|
||||||
|
if (r16 > 0xffff) {
|
||||||
|
r16 = 0xffff;
|
||||||
|
}
|
||||||
|
if (g16 > 0xffff) {
|
||||||
|
g16 = 0xffff;
|
||||||
|
}
|
||||||
|
if (b16 > 0xffff) {
|
||||||
|
b16 = 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Conversion Back to 8-bit.
|
||||||
|
uint8_t r8_final = (r8 == 255 && uint8_t(r16 >> 8) >= 254) ? 255 : uint8_t(r16 >> 8);
|
||||||
|
uint8_t g8_final = (g8 == 255 && uint8_t(g16 >> 8) >= 254) ? 255 : uint8_t(g16 >> 8);
|
||||||
|
uint8_t b8_final = (b8 == 255 && uint8_t(b16 >> 8) >= 254) ? 255 : uint8_t(b16 >> 8);
|
||||||
|
|
||||||
|
if (v8 == 1) {
|
||||||
|
// Linear tuning for the lowest possible brightness. x=y until
|
||||||
|
// the intersection point at 9.
|
||||||
|
if (r8 < 9 && r16 > 0) {
|
||||||
|
r8_final = r8;
|
||||||
|
}
|
||||||
|
if (g8 < 9 && g16 > 0) {
|
||||||
|
g8_final = g8;
|
||||||
|
}
|
||||||
|
if (b8 < 9 && b16 > 0) {
|
||||||
|
b8_final = b8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: Output
|
||||||
|
*out_r8 = r8_final;
|
||||||
|
*out_g8 = g8_final;
|
||||||
|
*out_b8 = b8_final;
|
||||||
|
*out_power_5bit = v8;
|
||||||
|
}
|
||||||
|
|
||||||
|
FASTLED_NAMESPACE_END
|
54
src/five_bit_hd_gamma.h
Normal file
54
src/five_bit_hd_gamma.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#ifndef _FIVE_BIT_HD_GAMMA_H_
|
||||||
|
#define _FIVE_BIT_HD_GAMMA_H_
|
||||||
|
|
||||||
|
#include "FastLED.h"
|
||||||
|
|
||||||
|
FASTLED_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
enum FiveBitGammaCorrectionMode {
|
||||||
|
kFiveBitGammaCorrectionMode_Null = 0,
|
||||||
|
kFiveBitGammaCorrectionMode_BitShift = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
// Applies gamma correction for the RGBV(8, 8, 8, 5) color space, where
|
||||||
|
// the last byte is the brightness byte at 5 bits.
|
||||||
|
// To override this five_bit_hd_gamma_bitshift function just define
|
||||||
|
// your own version anywhere in your project.
|
||||||
|
// Example:
|
||||||
|
// FASTLED_NAMESPACE_BEGIN
|
||||||
|
// void five_bit_hd_gamma_bitshift(
|
||||||
|
// uint8_t r8, uint8_t g8, uint8_t b8,
|
||||||
|
// uint8_t* out_r8,
|
||||||
|
// uint8_t* out_g8,
|
||||||
|
// uint8_t* out_b8,
|
||||||
|
// uint8_t* out_power_5bit) {
|
||||||
|
// cout << "hello world\n";
|
||||||
|
// }
|
||||||
|
// FASTLED_NAMESPACE_END
|
||||||
|
void five_bit_hd_gamma_bitshift(
|
||||||
|
uint8_t r8, uint8_t g8, uint8_t b8,
|
||||||
|
uint8_t* out_r8,
|
||||||
|
uint8_t* out_g8,
|
||||||
|
uint8_t* out_b8,
|
||||||
|
uint8_t* out_power_5bit) __attribute__((weak));
|
||||||
|
|
||||||
|
// Simple gamma correction function that converts from
|
||||||
|
// 8-bit color component and converts it to gamma corrected 16-bit
|
||||||
|
// color component. Fast and no memory overhead!
|
||||||
|
// To override this function just define your own version
|
||||||
|
// anywhere in your project.
|
||||||
|
// Example:
|
||||||
|
// FASTLED_NAMESPACE_BEGIN
|
||||||
|
// void five_bit_hd_gamma_function(
|
||||||
|
// uint8_t r8, uint8_t g8, uint8_t b8,
|
||||||
|
// uint16_t* r16, uint16_t* g16, uint16_t* b16) {
|
||||||
|
// cout << "hello world\n";
|
||||||
|
// }
|
||||||
|
// FASTLED_NAMESPACE_END
|
||||||
|
void five_bit_hd_gamma_function(
|
||||||
|
uint8_t r8, uint8_t g8, uint8_t b8,
|
||||||
|
uint16_t* r16, uint16_t* g16, uint16_t* b16) __attribute__((weak));
|
||||||
|
|
||||||
|
FASTLED_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // _FIVE_BIT_HD_GAMMA_H_
|
Loading…
Reference in New Issue
Block a user