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) {
if (!isCCT() || !correctWB) return;
if (k > 255) { //kelvin value, convert to 0-255
if (k < 1900) k = 1900;
if (k > 10091) k = 10091;
@ -1162,12 +1163,16 @@ void WS2812FX::service() {
uint16_t delay = FRAMETIME;
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
_colors_t[0] = gamma32(seg.currentColor(0));
_colors_t[1] = gamma32(seg.currentColor(1));
_colors_t[2] = gamma32(seg.currentColor(2));
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
// 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.
@ -1190,6 +1195,7 @@ void WS2812FX::service() {
#endif
seg.call++;
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;
@ -1198,7 +1204,6 @@ void WS2812FX::service() {
_segment_index++;
}
_virtualSegmentLength = 0;
BusManager::setSegmentCCT(-1);
_isServicing = false;
_triggered = false;
@ -1390,11 +1395,7 @@ bool WS2812FX::hasCCTBus(void) {
for (size_t b = 0; b < BusManager::getNumBusses(); b++) {
Bus *bus = BusManager::getBus(b);
if (bus == nullptr || bus->getLength()==0) break;
switch (bus->getType()) {
case TYPE_ANALOG_5CH:
case TYPE_ANALOG_2CH:
return true;
}
if (bus->hasCCT()) return true;
}
return false;
}

Wyświetl plik

@ -11,7 +11,6 @@
//colors.cpp
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
void colorRGBtoRGBW(byte* rgb);
//udp.cpp
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);
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;
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
@ -210,9 +209,10 @@ void BusDigital::show() {
if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits
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++) {
size_t offset = i*channels;
size_t offset = i * channels;
uint8_t co = _colorOrderMap.getPixelColorOrder(i+_start, _colorOrder);
uint32_t c;
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;
}
} 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;
if (_reversed) pix = _len - pix -1;
pix += _skip;
if (_type == TYPE_FW1906) Bus::calculateCCT(c, cctWW, cctCW);
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW);
}
#if !defined(STATUSLED) || STATUSLED>=0
if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
#endif
for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
_cct = oldCCT;
} else {
if (newBri < _bri) {
uint16_t hwLen = _len;
@ -241,7 +249,7 @@ void BusDigital::show() {
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
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
}
}
@ -282,17 +290,19 @@ void BusDigital::setStatusPixel(uint32_t c) {
void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid) return;
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 (_data) {
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
size_t offset = pix*channels;
if (Bus::hasRGB(_type)) {
size_t offset = pix * getNumberOfChannels();
if (hasRGB()) {
_data[offset++] = R(c);
_data[offset++] = G(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 {
if (_reversed) pix = _len - pix -1;
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;
}
}
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);
}
}
@ -316,13 +326,12 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) {
if (!_valid) return 0;
if (_data) {
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
size_t offset = pix*channels;
size_t offset = pix * getNumberOfChannels();
uint32_t c;
if (!Bus::hasRGB(_type)) {
if (!hasRGB()) {
c = RGBW32(_data[offset], _data[offset], _data[offset], _data[offset]);
} 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;
} else {
@ -640,12 +649,10 @@ uint32_t BusManager::memUsage(BusConfig &bc) {
if (bc.type == TYPE_ONOFF || IS_PWM(bc.type)) return 5;
uint16_t len = bc.count + bc.skipAmount;
uint16_t channels = 3;
uint16_t channels = Bus::getNumberOfChannels(bc.type);
uint16_t multiplier = 1;
if (IS_DIGITAL(bc.type)) { // digital types
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
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
multiplier = 5;
@ -654,11 +661,6 @@ uint32_t BusManager::memUsage(BusConfig &bc) {
multiplier = 2;
#endif
}
if (IS_VIRTUAL(bc.type)) {
switch (bc.type) {
case TYPE_NET_DDP_RGBW: channels = 4; break;
}
}
return len * channels * multiplier; //RGB
}
@ -720,7 +722,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
if (cct >= 0) {
//if white balance correction allowed, save as kelvin value instead of 0-255
if (allowWBCorrection) cct = 1900 + (cct << 5);
} else cct = -1;
} else cct = -1; // will use kelvin approximation from RGB
Bus::setCCT(cct);
}
@ -764,4 +766,4 @@ uint8_t BusManager::numBusses = 0;
Bus* BusManager::busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES];
ColorOrderMap BusManager::colorOrderMap = {};
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 getUsedCurrent() { 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 uint16_t getStart() { return _start; }
inline void setStart(uint16_t start) { _start = start; }
@ -157,9 +159,10 @@ class Bus {
}
virtual bool hasWhite(void) { return Bus::hasWhite(_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_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;
}
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;
return false;
}
static void setCCT(uint16_t cct) {
static int16_t getCCT() { return _cct; }
static void setCCT(int16_t cct) {
_cct = cct;
}
static void setCCTBlend(uint8_t b) {
@ -196,8 +200,7 @@ class Bus {
#else
//0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
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;
else cw = (cct * 255) / (255 - _cctBlend);
@ -220,8 +223,17 @@ class Bus {
bool _needsRefresh;
uint8_t _autoWhiteMode;
uint8_t *_data;
// global Auto White Calculation override
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;
// _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;
uint32_t autoWhiteCalc(uint32_t c);
@ -363,9 +375,12 @@ class BusManager {
static void setStatusPixel(uint32_t c);
static void setPixelColor(uint16_t pix, uint32_t c);
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 setMilliampsMax(uint16_t max) { _milliAmpsMax = max;}
static uint32_t getPixelColor(uint16_t pix);
static inline int16_t getSegmentCCT() { return Bus::getCCT(); }
static Bus* getBus(uint8_t busNr);