Introduce network type (previous virtual)

- phase shifting correction (limit to PWM CCT)
This commit is contained in:
Blaz Kristan 2024-08-27 14:11:56 +02:00
parent 820df0c596
commit b2e00eb868
2 changed files with 47 additions and 32 deletions

View File

@ -427,6 +427,9 @@ BusPwm::BusPwm(BusConfig &bc)
#else #else
ledcSetup(_ledcStart + i, _frequency, _depth); ledcSetup(_ledcStart + i, _frequency, _depth);
ledcAttachPin(_pins[i], _ledcStart + i); ledcAttachPin(_pins[i], _ledcStart + i);
// LEDC timer reset credit @dedehai
uint8_t group = ((_ledcStart + i) / 8), channel = ((_ledcStart + i) % 8); // _ledcStart + i is always less than MAX_LED_CHANNELS/LEDC_CHANNELS
ledc_timer_rst((ledc_mode_t)group, (ledc_channel_t)channel); // reset timer so all timers are almost in sync (for phase shift)
#endif #endif
} }
_hasRgb = hasRGB(bc.type); _hasRgb = hasRGB(bc.type);
@ -496,29 +499,30 @@ uint32_t BusPwm::getPixelColor(uint16_t pix) const {
void BusPwm::show() { void BusPwm::show() {
if (!_valid) return; if (!_valid) return;
unsigned numPins = getPins(); const unsigned numPins = getPins();
unsigned maxBri = (1<<_depth) - 1; const unsigned maxBri = (1<<_depth);
// use CIE brightness formula // use CIE brightness formula
unsigned pwmBri = (unsigned)_bri * 100; unsigned pwmBri = (unsigned)_bri * 100;
if(pwmBri < 2040) pwmBri = ((pwmBri << _depth) + 115043) / 230087; //adding '0.5' before division for correct rounding if (pwmBri < 2040)
pwmBri = ((pwmBri << _depth) + 115043) / 230087; //adding '0.5' before division for correct rounding
else { else {
pwmBri += 4080; pwmBri += 4080;
float temp = (float)pwmBri / 29580; float temp = (float)pwmBri / 29580;
temp = temp * temp * temp * (1<<_depth) - 1; temp = temp * temp * temp * maxBri;
pwmBri = (unsigned)temp; pwmBri = (unsigned)temp;
} }
// determine phase shift POC (credit @dedehai) // determine phase shift POC (credit @dedehai)
[[maybe_unused]] uint32_t phaseOffset = maxBri / numPins;
for (unsigned i = 0; i < numPins; i++) { for (unsigned i = 0; i < numPins; i++) {
unsigned scaled = (_data[i] * pwmBri) / 255; unsigned scaled = (_data[i] * pwmBri) / 255;
if (_reversed) scaled = maxBri - scaled; if (_reversed) scaled = maxBri - scaled;
#ifdef ESP8266 #ifdef ESP8266
analogWrite(_pins[i], scaled); analogWrite(_pins[i], scaled);
#else #else
if (_needsRefresh) { // hacked to determine if phase shifted PWM is requested // CCT blending has to be 0 for phse shift to work (WW & CW must not overlap)
if (_type == TYPE_ANALOG_2CH && _needsRefresh && Bus::getCCTBlend() == 0) { // hacked to determine if phase shifted PWM is requested
if (scaled >= maxBri/2) scaled = maxBri/2 - 1; // safety check & add dead time of 1 pulse
uint8_t group = ((_ledcStart + i) / 8), channel = ((_ledcStart + i) % 8); // _ledcStart + i is always less than MAX_LED_CHANNELS/LEDC_CHANNELS uint8_t group = ((_ledcStart + i) / 8), channel = ((_ledcStart + i) % 8); // _ledcStart + i is always less than MAX_LED_CHANNELS/LEDC_CHANNELS
ledc_set_duty_with_hpoint((ledc_mode_t)group, (ledc_channel_t)channel, scaled, phaseOffset*i); ledc_set_duty_and_update((ledc_mode_t)group, (ledc_channel_t)channel, scaled, (maxBri / numPins)*i);
ledc_update_duty((ledc_mode_t)group, (ledc_channel_t)channel);
} else } else
ledcWrite(_ledcStart + i, scaled); ledcWrite(_ledcStart + i, scaled);
#endif #endif
@ -719,8 +723,8 @@ String BusManager::getLEDTypesJSONString(void) {
{TYPE_WS2805, "D", PSTR("WS2805 RGBCW")}, {TYPE_WS2805, "D", PSTR("WS2805 RGBCW")},
{TYPE_SM16825, "D", PSTR("SM16825 RGBCW")}, {TYPE_SM16825, "D", PSTR("SM16825 RGBCW")},
{TYPE_WS2812_1CH_X3, "D", PSTR("WS2811 White")}, {TYPE_WS2812_1CH_X3, "D", PSTR("WS2811 White")},
//{TYPE_WS2812_2CH_X3, "D", PSTR("WS2811 CCT")}, //{TYPE_WS2812_2CH_X3, "D", PSTR("WS2811 CCT")}, // not implemented
//{TYPE_WS2812_WWA, "D", PSTR("WS2811 WWA")}, //{TYPE_WS2812_WWA, "D", PSTR("WS2811 WWA")}, // not implemented
{TYPE_WS2801, "2P", PSTR("WS2801")}, {TYPE_WS2801, "2P", PSTR("WS2801")},
{TYPE_APA102, "2P", PSTR("APA102")}, {TYPE_APA102, "2P", PSTR("APA102")},
{TYPE_LPD8806, "2P", PSTR("LPD8806")}, {TYPE_LPD8806, "2P", PSTR("LPD8806")},
@ -732,11 +736,15 @@ String BusManager::getLEDTypesJSONString(void) {
{TYPE_ANALOG_3CH, "AAA", PSTR("PWM RGB")}, {TYPE_ANALOG_3CH, "AAA", PSTR("PWM RGB")},
{TYPE_ANALOG_4CH, "AAAA", PSTR("PWM RGBW")}, {TYPE_ANALOG_4CH, "AAAA", PSTR("PWM RGBW")},
{TYPE_ANALOG_5CH, "AAAAA", PSTR("PWM RGB+CCT")}, {TYPE_ANALOG_5CH, "AAAAA", PSTR("PWM RGB+CCT")},
//{TYPE_ANALOG_6CH, "AAAAAA", PSTR("PWM RGB+DCCT")}, //{TYPE_ANALOG_6CH, "AAAAAA", PSTR("PWM RGB+DCCT")}, // unimplementable ATM
{TYPE_NET_DDP_RGB, "V", PSTR("DDP RGB (network)")}, {TYPE_NET_DDP_RGB, "N", PSTR("DDP RGB (network)")},
{TYPE_NET_ARTNET_RGB, "V", PSTR("Art-Net RGB (network)")}, {TYPE_NET_ARTNET_RGB, "N", PSTR("Art-Net RGB (network)")},
{TYPE_NET_DDP_RGBW, "V", PSTR("DDP RGBW (network)")}, {TYPE_NET_DDP_RGBW, "N", PSTR("DDP RGBW (network)")},
{TYPE_NET_ARTNET_RGBW, "V", PSTR("Art-Net RGBW (network)")} {TYPE_NET_ARTNET_RGBW, "N", PSTR("Art-Net RGBW (network)")},
// hypothetical extensions
//{TYPE_VIRTUAL_I2C_W, "V", PSTR("I2C White (virtual)")}, // allows setting I2C address in _pin[0]
//{TYPE_VIRTUAL_I2C_CCT, "V", PSTR("I2C CCT (virtual)")}, // allows setting I2C address in _pin[0]
//{TYPE_VIRTUAL_I2C_RGB, "V", PSTR("I2C RGB (virtual)")}, // allows setting I2C address in _pin[0]
}; };
String json = "["; String json = "[";
for (const auto &type : types) { for (const auto &type : types) {

View File

@ -24,7 +24,8 @@
function isAna(t) { return gT(t).t === "" || isPWM(t); } // is analog type function isAna(t) { return gT(t).t === "" || isPWM(t); } // is analog type
function isDig(t) { return gT(t).t === "D" || isD2P(t); } // is digital type function isDig(t) { return gT(t).t === "D" || isD2P(t); } // is digital type
function isD2P(t) { return gT(t).t === "2P"; } // is digital 2 pin type function isD2P(t) { return gT(t).t === "2P"; } // is digital 2 pin type
function isVir(t) { return gT(t).t === "V"; } // is virtual type function isNet(t) { return gT(t).t === "N"; } // is network type
function isVir(t) { return gT(t).t === "V" || isNet(t); } // is virtual type
function hasRGB(t) { return !!(gT(t).c & 0x01); } // has RGB function hasRGB(t) { return !!(gT(t).c & 0x01); } // has RGB
function hasW(t) { return !!(gT(t).c & 0x02); } // has white channel function hasW(t) { return !!(gT(t).c & 0x02); } // has white channel
function hasCCT(t) { return !!(gT(t).c & 0x04); } // is white CCT enabled function hasCCT(t) { return !!(gT(t).c & 0x04); } // is white CCT enabled
@ -202,12 +203,10 @@
let len = parseInt(d.getElementsByName("LC"+n)[0].value); let len = parseInt(d.getElementsByName("LC"+n)[0].value);
len += parseInt(d.getElementsByName("SL"+n)[0].value); // skipped LEDs are allocated too len += parseInt(d.getElementsByName("SL"+n)[0].value); // skipped LEDs are allocated too
let dbl = 0; let dbl = 0;
let ch = 3; let ch = 3*hasRGB(t) + hasW(t) + hasCCT(t);
let mul = 1; let mul = 1;
if (isDig(t)) { if (isDig(t)) {
if (is16b(t)) len *= 2; // 16 bit LEDs if (is16b(t)) len *= 2; // 16 bit LEDs
if (t > 28 && t < 40) ch = 4; //RGBW
if (t == 28) ch = 5; //GRBCW
if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem
mul = 5; mul = 5;
} }
@ -216,7 +215,6 @@
} }
if (d.Sf.LD.checked) dbl = len * ch; // double buffering if (d.Sf.LD.checked) dbl = len * ch; // double buffering
} }
if (isVir(t) && t == 88) ch = 4;
return len * ch * mul + dbl; return len * ch * mul + dbl;
} }
@ -232,27 +230,36 @@
let p1d = ""; let p1d = "";
let off = "Off Refresh"; let off = "Off Refresh";
switch (gT(t).t.charAt(0)) { switch (gT(t).t.charAt(0)) {
case '2': case '2': // 2 pin digital
p1d = "Clock "+p0d; p1d = "Clock "+p0d;
case 'D': // fallthrough
case 'D': // digital
p0d = "Data "+p0d; p0d = "Data "+p0d;
break; break;
case 'A': case 'A': // PWM analog
if (gT(t).t.length > 1) { switch (gT(t).t.length) { // type length determines number of GPIO used
p0d = "GPIOs:"; case 1: break;
off = "Phase shift"; case 2: off = "Phase shift";
} else gId(`dig${n}f`).style.display = "none"; if (d.Sf["CB"].value != 0) gId(`rf${n}`).checked = 0; // disable phase shifting
gId(`rf${n}`).disabled = (d.Sf["CB"].value != 0); // prevent changes
// fallthrough
default: p0d = "GPIOs:"; break;
}
// PWM CCT allows phase shifting
gId(`dig${n}f`).style.display = (gT(t).t.length != 2) ? "none" : "inline";
break; break;
case 'V': case 'N': // network
p0d = "IP address:"; p0d = "IP address:";
break; break;
case 'V': // virtual/non-GPIO based
p0d = "Config:"
break;
} }
gId("p0d"+n).innerText = p0d; gId("p0d"+n).innerText = p0d;
gId("p1d"+n).innerText = p1d; gId("p1d"+n).innerText = p1d;
gId("off"+n).innerText = off; gId("off"+n).innerText = off;
// secondary pins show/hide (type string length is equivalent to number of pins used; except for virtual and on/off) // secondary pins show/hide (type string length is equivalent to number of pins used; except for network and on/off)
let pins = gT(t).t.length + 3*isVir(t); // fixes virtual pins to 4 let pins = Math.min(gT(t).t.length,1) + 3*isNet(t); // fixes network pins to 4
if (pins == 0) pins = 1; // fixes on/off pin
for (let p=1; p<5; p++) { for (let p=1; p<5; p++) {
var LK = d.Sf["L"+p+n]; var LK = d.Sf["L"+p+n];
if (!LK) continue; if (!LK) continue;
@ -909,7 +916,7 @@ Swap: <select id="xw${s}" name="XW${s}">
<br> <br>
Calculate CCT from RGB: <input type="checkbox" name="CR"><br> Calculate CCT from RGB: <input type="checkbox" name="CR"><br>
CCT IC used (Athom 15W): <input type="checkbox" name="IC"><br> CCT IC used (Athom 15W): <input type="checkbox" name="IC"><br>
CCT additive blending: <input type="number" class="s" min="0" max="100" name="CB" required> % CCT additive blending: <input type="number" class="s" min="0" max="100" name="CB" onchange="UI()" required> %
</div> </div>
<h3>Advanced</h3> <h3>Advanced</h3>
Palette blending: Palette blending: