Modify Bus & BusManager to accommodate digital CCT

- additional fix in hasWhite() & setCCT() & hasCCT()
pull/3810/head
Blaz Kristan 2024-03-16 12:36:05 +01:00
rodzic 505768db04
commit 0dcb56eab5
3 zmienionych plików z 57 dodań i 39 usunięć

Wyświetl plik

@ -538,6 +538,7 @@ bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed
} }
void Segment::setCCT(uint16_t k) { void Segment::setCCT(uint16_t k) {
if (!isCCT() || !correctWB) return;
if (k > 255) { //kelvin value, convert to 0-255 if (k > 255) { //kelvin value, convert to 0-255
if (k < 1900) k = 1900; if (k < 1900) k = 1900;
if (k > 10091) k = 10091; if (k > 10091) k = 10091;
@ -1162,12 +1163,16 @@ void WS2812FX::service() {
uint16_t delay = FRAMETIME; uint16_t delay = FRAMETIME;
if (!seg.freeze) { //only run effect function if not frozen if (!seg.freeze) { //only run effect function if not frozen
int16_t oldCCT = BusManager::getSegmentCCT(); // store original CCT value (actually it is not Segment based)
_virtualSegmentLength = seg.virtualLength(); //SEGLEN _virtualSegmentLength = seg.virtualLength(); //SEGLEN
_colors_t[0] = gamma32(seg.currentColor(0)); _colors_t[0] = gamma32(seg.currentColor(0));
_colors_t[1] = gamma32(seg.currentColor(1)); _colors_t[1] = gamma32(seg.currentColor(1));
_colors_t[2] = gamma32(seg.currentColor(2)); _colors_t[2] = gamma32(seg.currentColor(2));
seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference
if (!cctFromRgb || correctWB) BusManager::setSegmentCCT(seg.currentBri(true), correctWB); // when correctWB is true we need to correct/adjust RGB value according to desired CCT value, but it will also affect actual WW/CW ratio
// when cctFromRgb is true we implicitly calculate WW and CW from RGB values
if (cctFromRgb) BusManager::setSegmentCCT(-1);
else BusManager::setSegmentCCT(seg.currentBri(true), correctWB);
// Effect blending // Effect blending
// When two effects are being blended, each may have different segment data, this // When two effects are being blended, each may have different segment data, this
// data needs to be saved first and then restored before running previous mode. // data needs to be saved first and then restored before running previous mode.
@ -1190,6 +1195,7 @@ void WS2812FX::service() {
#endif #endif
seg.call++; seg.call++;
if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments
} }
seg.next_time = nowUp + delay; seg.next_time = nowUp + delay;
@ -1198,7 +1204,6 @@ void WS2812FX::service() {
_segment_index++; _segment_index++;
} }
_virtualSegmentLength = 0; _virtualSegmentLength = 0;
BusManager::setSegmentCCT(-1);
_isServicing = false; _isServicing = false;
_triggered = false; _triggered = false;
@ -1390,11 +1395,7 @@ bool WS2812FX::hasCCTBus(void) {
for (size_t b = 0; b < BusManager::getNumBusses(); b++) { for (size_t b = 0; b < BusManager::getNumBusses(); b++) {
Bus *bus = BusManager::getBus(b); Bus *bus = BusManager::getBus(b);
if (bus == nullptr || bus->getLength()==0) break; if (bus == nullptr || bus->getLength()==0) break;
switch (bus->getType()) { if (bus->hasCCT()) return true;
case TYPE_ANALOG_5CH:
case TYPE_ANALOG_2CH:
return true;
}
} }
return false; return false;
} }

Wyświetl plik

@ -11,7 +11,6 @@
//colors.cpp //colors.cpp
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
void colorRGBtoRGBW(byte* rgb);
//udp.cpp //udp.cpp
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false); uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false);
@ -122,7 +121,7 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
} }
_iType = PolyBus::getI(bc.type, _pins, nr); _iType = PolyBus::getI(bc.type, _pins, nr);
if (_iType == I_NONE) return; if (_iType == I_NONE) return;
if (bc.doubleBuffer && !allocData(bc.count * (Bus::hasWhite(_type) + 3*Bus::hasRGB(_type)))) return; //warning: hardcoded channel count if (bc.doubleBuffer && !allocData(bc.count * Bus::getNumberOfChannels(bc.type))) return;
//_buffering = bc.doubleBuffer; //_buffering = bc.doubleBuffer;
uint16_t lenToCreate = bc.count; uint16_t lenToCreate = bc.count;
if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus
@ -210,9 +209,10 @@ void BusDigital::show() {
if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits
if (_data) { if (_data) {
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); size_t channels = getNumberOfChannels();
int16_t oldCCT = _cct; // temporarily save bus CCT
for (size_t i=0; i<_len; i++) { for (size_t i=0; i<_len; i++) {
size_t offset = i*channels; size_t offset = i * channels;
uint8_t co = _colorOrderMap.getPixelColorOrder(i+_start, _colorOrder); uint8_t co = _colorOrderMap.getPixelColorOrder(i+_start, _colorOrder);
uint32_t c; uint32_t c;
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs (_len is always a multiple of 3) if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs (_len is always a multiple of 3)
@ -222,18 +222,26 @@ void BusDigital::show() {
case 2: c = RGBW32(_data[offset-2], _data[offset-1], _data[offset] , 0); break; case 2: c = RGBW32(_data[offset-2], _data[offset-1], _data[offset] , 0); break;
} }
} else { } else {
c = RGBW32(_data[offset],_data[offset+1],_data[offset+2],(Bus::hasWhite(_type)?_data[offset+3]:0)); if (hasRGB()) c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], hasWhite() ? _data[offset+3] : 0);
else c = RGBW32(0, 0, 0, _data[offset]);
}
if (hasCCT()) {
// unfortunately as a segment may span multiple buses or a bus may contain multiple segments and each segment may have different CCT
// we need to extract and appy CCT value for each pixel individually even though all buses share the same _cct variable
// TODO: there is an issue if CCT is calculated from RGB value (_cct==-1), we cannot do that with double buffer
_cct = _data[offset+channels-1];
Bus::calculateCCT(c, cctWW, cctCW);
} }
uint16_t pix = i; uint16_t pix = i;
if (_reversed) pix = _len - pix -1; if (_reversed) pix = _len - pix -1;
pix += _skip; pix += _skip;
if (_type == TYPE_FW1906) Bus::calculateCCT(c, cctWW, cctCW);
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW); PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW);
} }
#if !defined(STATUSLED) || STATUSLED>=0 #if !defined(STATUSLED) || STATUSLED>=0
if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
#endif #endif
for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
_cct = oldCCT;
} else { } else {
if (newBri < _bri) { if (newBri < _bri) {
uint16_t hwLen = _len; uint16_t hwLen = _len;
@ -241,7 +249,7 @@ void BusDigital::show() {
for (unsigned i = 0; i < hwLen; i++) { for (unsigned i = 0; i < hwLen; i++) {
// use 0 as color order, actual order does not matter here as we just update the channel values as-is // use 0 as color order, actual order does not matter here as we just update the channel values as-is
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0), _bri); uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0), _bri);
if (_type == TYPE_FW1906) Bus::calculateCCT(c, cctWW, cctCW); if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW); // this will unfortunately corrupt (segment) CCT data on every bus
PolyBus::setPixelColor(_busPtr, _iType, i, c, 0, (cctCW<<8) | cctWW); // repaint all pixels with new brightness PolyBus::setPixelColor(_busPtr, _iType, i, c, 0, (cctCW<<8) | cctWW); // repaint all pixels with new brightness
} }
} }
@ -282,17 +290,19 @@ void BusDigital::setStatusPixel(uint32_t c) {
void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid) return; if (!_valid) return;
uint8_t cctWW = 0, cctCW = 0; uint8_t cctWW = 0, cctCW = 0;
if (Bus::hasWhite(_type)) c = autoWhiteCalc(c); if (hasWhite()) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
if (_data) { if (_data) {
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); size_t offset = pix * getNumberOfChannels();
size_t offset = pix*channels; if (hasRGB()) {
if (Bus::hasRGB(_type)) {
_data[offset++] = R(c); _data[offset++] = R(c);
_data[offset++] = G(c); _data[offset++] = G(c);
_data[offset++] = B(c); _data[offset++] = B(c);
} }
if (Bus::hasWhite(_type)) _data[offset] = W(c); if (hasWhite()) _data[offset++] = W(c);
// unfortunately as a segment may span multiple buses or a bus may contain multiple segments and each segment may have different CCT
// we need to store CCT value for each pixel (if there is a color correction in play, convert K in CCT ratio)
if (hasCCT()) _data[offset] = _cct >= 1900 ? (_cct - 1900) >> 5 : (_cct < 0 ? 127 : _cct); // TODO: if _cct == -1 we simply ignore it
} else { } else {
if (_reversed) pix = _len - pix -1; if (_reversed) pix = _len - pix -1;
pix += _skip; pix += _skip;
@ -307,7 +317,7 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break; case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break;
} }
} }
if (_type == TYPE_FW1906) Bus::calculateCCT(c, cctWW, cctCW); if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW);
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW); PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW);
} }
} }
@ -316,13 +326,12 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) { uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) {
if (!_valid) return 0; if (!_valid) return 0;
if (_data) { if (_data) {
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); size_t offset = pix * getNumberOfChannels();
size_t offset = pix*channels;
uint32_t c; uint32_t c;
if (!Bus::hasRGB(_type)) { if (!hasRGB()) {
c = RGBW32(_data[offset], _data[offset], _data[offset], _data[offset]); c = RGBW32(_data[offset], _data[offset], _data[offset], _data[offset]);
} else { } else {
c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], Bus::hasWhite(_type) ? _data[offset+3] : 0); c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], hasWhite() ? _data[offset+3] : 0);
} }
return c; return c;
} else { } else {
@ -640,12 +649,10 @@ uint32_t BusManager::memUsage(BusConfig &bc) {
if (bc.type == TYPE_ONOFF || IS_PWM(bc.type)) return 5; if (bc.type == TYPE_ONOFF || IS_PWM(bc.type)) return 5;
uint16_t len = bc.count + bc.skipAmount; uint16_t len = bc.count + bc.skipAmount;
uint16_t channels = 3; uint16_t channels = Bus::getNumberOfChannels(bc.type);
uint16_t multiplier = 1; uint16_t multiplier = 1;
if (IS_DIGITAL(bc.type)) { // digital types if (IS_DIGITAL(bc.type)) { // digital types
if (IS_16BIT(bc.type)) len *= 2; // 16-bit LEDs if (IS_16BIT(bc.type)) len *= 2; // 16-bit LEDs
if (bc.type > 28) channels = 4; //RGBW
if (bc.type == TYPE_FW1906) channels = 5; //GRBCW
#ifdef ESP8266 #ifdef ESP8266
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
multiplier = 5; multiplier = 5;
@ -654,11 +661,6 @@ uint32_t BusManager::memUsage(BusConfig &bc) {
multiplier = 2; multiplier = 2;
#endif #endif
} }
if (IS_VIRTUAL(bc.type)) {
switch (bc.type) {
case TYPE_NET_DDP_RGBW: channels = 4; break;
}
}
return len * channels * multiplier; //RGB return len * channels * multiplier; //RGB
} }
@ -720,7 +722,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
if (cct >= 0) { if (cct >= 0) {
//if white balance correction allowed, save as kelvin value instead of 0-255 //if white balance correction allowed, save as kelvin value instead of 0-255
if (allowWBCorrection) cct = 1900 + (cct << 5); if (allowWBCorrection) cct = 1900 + (cct << 5);
} else cct = -1; } else cct = -1; // will use kelvin approximation from RGB
Bus::setCCT(cct); Bus::setCCT(cct);
} }
@ -764,4 +766,4 @@ uint8_t BusManager::numBusses = 0;
Bus* BusManager::busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; Bus* BusManager::busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES];
ColorOrderMap BusManager::colorOrderMap = {}; ColorOrderMap BusManager::colorOrderMap = {};
uint16_t BusManager::_milliAmpsUsed = 0; uint16_t BusManager::_milliAmpsUsed = 0;
uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT; uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT;

Wyświetl plik

@ -141,6 +141,8 @@ class Bus {
virtual uint16_t getLEDCurrent() { return 0; } virtual uint16_t getLEDCurrent() { return 0; }
virtual uint16_t getUsedCurrent() { return 0; } virtual uint16_t getUsedCurrent() { return 0; }
virtual uint16_t getMaxCurrent() { return 0; } virtual uint16_t getMaxCurrent() { return 0; }
virtual uint8_t getNumberOfChannels() { return hasWhite(_type) + 3*hasRGB(_type) + hasCCT(_type); }
static inline uint8_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
inline void setReversed(bool reversed) { _reversed = reversed; } inline void setReversed(bool reversed) { _reversed = reversed; }
inline uint16_t getStart() { return _start; } inline uint16_t getStart() { return _start; }
inline void setStart(uint16_t start) { _start = start; } inline void setStart(uint16_t start) { _start = start; }
@ -157,9 +159,10 @@ class Bus {
} }
virtual bool hasWhite(void) { return Bus::hasWhite(_type); } virtual bool hasWhite(void) { return Bus::hasWhite(_type); }
static bool hasWhite(uint8_t type) { static bool hasWhite(uint8_t type) {
if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904 || type == TYPE_FW1906) return true; // digital types with white channel if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) ||
type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904 || type == TYPE_FW1906) return true; // digital types with white channel
if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel
if (type == TYPE_NET_DDP_RGBW) return true; // network types with white channel if (type == TYPE_NET_DDP_RGBW || type == TYPE_NET_ARTNET_RGBW) return true; // network types with white channel
return false; return false;
} }
virtual bool hasCCT(void) { return Bus::hasCCT(_type); } virtual bool hasCCT(void) { return Bus::hasCCT(_type); }
@ -168,7 +171,8 @@ class Bus {
type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH || type == TYPE_FW1906) return true; type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH || type == TYPE_FW1906) return true;
return false; return false;
} }
static void setCCT(uint16_t cct) { static int16_t getCCT() { return _cct; }
static void setCCT(int16_t cct) {
_cct = cct; _cct = cct;
} }
static void setCCTBlend(uint8_t b) { static void setCCTBlend(uint8_t b) {
@ -196,8 +200,7 @@ class Bus {
#else #else
//0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold) //0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
if (cct < _cctBlend) ww = 255; if (cct < _cctBlend) ww = 255;
else ww = ((255-cct) * 255) / (255 - _cctBlend); else ww = ((255-cct) * 255) / (255 - _cctBlend);
if ((255-cct) < _cctBlend) cw = 255; if ((255-cct) < _cctBlend) cw = 255;
else cw = (cct * 255) / (255 - _cctBlend); else cw = (cct * 255) / (255 - _cctBlend);
@ -220,8 +223,17 @@ class Bus {
bool _needsRefresh; bool _needsRefresh;
uint8_t _autoWhiteMode; uint8_t _autoWhiteMode;
uint8_t *_data; uint8_t *_data;
// global Auto White Calculation override
static uint8_t _gAWM; static uint8_t _gAWM;
// _cct has the following menaings (see calculateCCT() & BusManager::setSegmentCCT()):
// -1 means to extract approximate CCT value in K from RGB (in calcualteCCT())
// [0,255] is the exact CCT value where 0 means warm and 255 cold
// [1900,10060] only for color correction expressed in K (colorBalanceFromKelvin())
static int16_t _cct; static int16_t _cct;
// _cctBlend determines WW/CW blending:
// 0 - linear (CCT 127 => 50% warm, 50% cold)
// 63 - semi additive/nonlinear (CCT 127 => 66% warm, 66% cold)
// 127 - additive CCT blending (CCT 127 => 100% warm, 100% cold)
static uint8_t _cctBlend; static uint8_t _cctBlend;
uint32_t autoWhiteCalc(uint32_t c); uint32_t autoWhiteCalc(uint32_t c);
@ -363,9 +375,12 @@ class BusManager {
static void setStatusPixel(uint32_t c); static void setStatusPixel(uint32_t c);
static void setPixelColor(uint16_t pix, uint32_t c); static void setPixelColor(uint16_t pix, uint32_t c);
static void setBrightness(uint8_t b); static void setBrightness(uint8_t b);
// for setSegmentCCT(), cct can only be in [-1,255] range; allowWBCorrection will convert it to K
// WARNING: setSegmentCCT() is a misleading name!!! much better would be setGlobalCCT() or just setCCT()
static void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); static void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
static void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;} static void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;}
static uint32_t getPixelColor(uint16_t pix); static uint32_t getPixelColor(uint16_t pix);
static inline int16_t getSegmentCCT() { return Bus::getCCT(); }
static Bus* getBus(uint8_t busNr); static Bus* getBus(uint8_t busNr);