Skip to content

Commit

Permalink
Better low brightness level PWM handling
Browse files Browse the repository at this point in the history
Additional string optimisation
  • Loading branch information
blazoncek committed Mar 7, 2024
1 parent da6d64e commit c0beb62
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 45 deletions.
77 changes: 49 additions & 28 deletions wled00/bus_manager.cpp
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -376,14 +376,22 @@ BusPwm::BusPwm(BusConfig &bc)
_frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ;

#ifdef ESP8266
analogWriteRange(255); //same range as one RGB channel
// duty cycle resolution (_depth) can be extracted from this formula: 1MHz > _frequency * 2^_depth
if (_frequency > 1760) _depth = 8;
else if (_frequency > 880) _depth = 9;
else _depth = 10; // WLED_PWM_FREQ <= 880Hz
analogWriteRange((1<<_depth)-1);
analogWriteFreq(_frequency);
#else
_ledcStart = pinManager.allocateLedc(numPins);
if (_ledcStart == 255) { //no more free LEDC channels
deallocatePins(); return;
}
//_prevBri = _bri;
// duty cycle resolution (_depth) can be extracted from this formula: 80MHz > _frequency * 2^_depth
if (_frequency > 78124) _depth = 9;
else if (_frequency > 39062) _depth = 10;
else if (_frequency > 19531) _depth = 11;
else _depth = 12; // WLED_PWM_FREQ <= 19531Hz
#endif

for (unsigned i = 0; i < numPins; i++) {
Expand All @@ -395,14 +403,6 @@ BusPwm::BusPwm(BusConfig &bc)
#ifdef ESP8266
pinMode(_pins[i], OUTPUT);
#else
switch (_frequency) {
case WLED_PWM_FREQ/3: _depth = 12; break; // 6510Hz
case WLED_PWM_FREQ/2: _depth = 11; break; // 9676Hz
default:
case WLED_PWM_FREQ: _depth = 10; break; // 19531Hz
case WLED_PWM_FREQ*4/3: _depth = 9; break; // 26041Hz
case WLED_PWM_FREQ*2: _depth = 8; break; // 39062Hz
}
ledcSetup(_ledcStart + i, _frequency, _depth);
ledcAttachPin(_pins[i], _ledcStart + i);
#endif
Expand All @@ -411,13 +411,6 @@ BusPwm::BusPwm(BusConfig &bc)
_valid = true;
}

void BusPwm::setBrightness(uint8_t bri) {
#ifdef ARDUINO_ARCH_ESP32
//_prevBri = _bri;
#endif
Bus::setBrightness(bri);
}

void BusPwm::setPixelColor(uint16_t pix, uint32_t c) {
if (pix != 0 || !_valid) return; //only react to first pixel
if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c);
Expand Down Expand Up @@ -477,24 +470,52 @@ uint32_t BusPwm::getPixelColor(uint16_t pix) {
return RGBW32(_data[0], _data[1], _data[2], _data[3]);
}

#ifndef ESP8266
static const uint16_t cieLUT[256] = {
0, 2, 4, 5, 7, 9, 11, 13, 15, 16,
18, 20, 22, 24, 26, 27, 29, 31, 33, 35,
34, 36, 37, 39, 41, 43, 45, 47, 49, 52,
54, 56, 59, 61, 64, 67, 69, 72, 75, 78,
81, 84, 87, 90, 94, 97, 100, 104, 108, 111,
115, 119, 123, 127, 131, 136, 140, 144, 149, 154,
158, 163, 168, 173, 178, 183, 189, 194, 200, 205,
211, 217, 223, 229, 235, 241, 247, 254, 261, 267,
274, 281, 288, 295, 302, 310, 317, 325, 333, 341,
349, 357, 365, 373, 382, 391, 399, 408, 417, 426,
436, 445, 455, 464, 474, 484, 494, 505, 515, 526,
536, 547, 558, 569, 580, 592, 603, 615, 627, 639,
651, 663, 676, 689, 701, 714, 727, 741, 754, 768,
781, 795, 809, 824, 838, 853, 867, 882, 897, 913,
928, 943, 959, 975, 991, 1008, 1024, 1041, 1058, 1075,
1092, 1109, 1127, 1144, 1162, 1180, 1199, 1217, 1236, 1255,
1274, 1293, 1312, 1332, 1352, 1372, 1392, 1412, 1433, 1454,
1475, 1496, 1517, 1539, 1561, 1583, 1605, 1628, 1650, 1673,
1696, 1719, 1743, 1767, 1791, 1815, 1839, 1864, 1888, 1913,
1939, 1964, 1990, 2016, 2042, 2068, 2095, 2121, 2148, 2176,
2203, 2231, 2259, 2287, 2315, 2344, 2373, 2402, 2431, 2461,
2491, 2521, 2551, 2581, 2612, 2643, 2675, 2706, 2738, 2770,
2802, 2835, 2867, 2900, 2934, 2967, 3001, 3035, 3069, 3104,
3138, 3174, 3209, 3244, 3280, 3316, 3353, 3389, 3426, 3463,
3501, 3539, 3576, 3615, 3653, 3692, 3731, 3770, 3810, 3850,
3890, 3930, 3971, 4012, 4053, 4095
};
#endif

void BusPwm::show() {
if (!_valid) return;
uint8_t numPins = NUM_PWM_PINS(_type);
unsigned maxBri = (1<<_depth) - 1;
#ifdef ESP8266
unsigned pwmBri = (unsigned)(roundf(powf((float)_bri / 255.0f, 1.7f) * (float)maxBri + 0.5f)); // using gamma 1.7 to extrapolate PWM duty cycle
#else
unsigned pwmBri = cieLUT[_bri] >> (12 - _depth); // use CIE LUT
#endif
for (unsigned i = 0; i < numPins; i++) {
unsigned scaled = (_data[i] * pwmBri) / 255;
if (_reversed) scaled = maxBri - scaled;
#ifdef ESP8266
uint8_t scaled = (_data[i] * _bri) / 255;
if (_reversed) scaled = 255 - scaled;
analogWrite(_pins[i], scaled);
#else
unsigned scaled = ((_data[i] * _bri) << (_depth-8)) / 255;
if (_reversed) scaled = (1<<_depth) - 1 - scaled;
// TODO: implement ledcFade() if the brightness changed between calls
// bool ledcFade(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms);
// if (_prevBri != _bri) {
// unsigned prevScaled = ((_data[i] * _prevBri) << (_depth-8)) / 255;
// ledcFade(_ledcStart + i, prevScaled, scaled, FRAMETIME-1); // frametime is a macro expanding to strip.getFrameTime() which is unwanted here
// _prevBri = _bri;
// } else
ledcWrite(_ledcStart + i, scaled);
#endif
}
Expand Down
4 changes: 1 addition & 3 deletions wled00/bus_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,6 @@ class BusPwm : public Bus {
BusPwm(BusConfig &bc);
~BusPwm() { cleanup(); }

void setBrightness(uint8_t bri) override;
void setPixelColor(uint16_t pix, uint32_t c) override;
uint32_t getPixelColor(uint16_t pix) override; //does no index check
uint8_t getPins(uint8_t* pinArray) override;
Expand All @@ -268,9 +267,8 @@ class BusPwm : public Bus {
uint8_t _pwmdata[5];
#ifdef ARDUINO_ARCH_ESP32
uint8_t _ledcStart;
uint8_t _depth;
//uint8_t _prevBri;
#endif
uint8_t _depth;
uint16_t _frequency;

void deallocatePins();
Expand Down
16 changes: 11 additions & 5 deletions wled00/cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
uint8_t ledType = elm["type"] | TYPE_WS2812_RGB;
bool reversed = elm["rev"];
bool refresh = elm["ref"] | false;
uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM (not yet implemented fully)
uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM
uint8_t AWmode = elm[F("rgbwm")] | RGBW_MODE_MANUAL_ONLY;
uint8_t maPerLed = elm[F("ledma")] | 55;
uint16_t maMax = elm[F("maxpwr")] | (ablMilliampsMax * length) / total; // rough (incorrect?) per strip ABL calculation when no config exists
Expand Down Expand Up @@ -627,6 +627,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
return (doc["sv"] | true);
}


static const char s_cfg_json[] PROGMEM = "/cfg.json";

void deserializeConfigFromFS() {
bool success = deserializeConfigSec();
if (!success) { //if file does not exist, try reading from EEPROM
Expand All @@ -640,7 +643,7 @@ void deserializeConfigFromFS() {

DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));

success = readObjectFromFile(PSTR("/cfg.json"), nullptr, pDoc);
success = readObjectFromFile(s_cfg_json, nullptr, pDoc);
if (!success) { // if file does not exist, optionally try reading from EEPROM and then save defaults to FS
releaseJSONBufferLock();
#ifdef WLED_ADD_EEPROM_SUPPORT
Expand Down Expand Up @@ -1065,21 +1068,24 @@ void serializeConfig() {
JsonObject usermods_settings = root.createNestedObject("um");
usermods.addToConfig(usermods_settings);

File f = WLED_FS.open(SET_F("/cfg.json"), "w");
File f = WLED_FS.open(FPSTR(s_cfg_json), "w");
if (f) serializeJson(root, f);
f.close();
releaseJSONBufferLock();

doSerializeConfig = false;
}


static const char s_wsec_json[] PROGMEM = "/wsec.json";

//settings in /wsec.json, not accessible via webserver, for passwords and tokens
bool deserializeConfigSec() {
DEBUG_PRINTLN(F("Reading settings from /wsec.json..."));

if (!requestJSONBufferLock(3)) return false;

bool success = readObjectFromFile(PSTR("/wsec.json"), nullptr, pDoc);
bool success = readObjectFromFile(s_wsec_json, nullptr, pDoc);
if (!success) {
releaseJSONBufferLock();
return false;
Expand Down Expand Up @@ -1162,7 +1168,7 @@ void serializeConfigSec() {
ota[F("lock-wifi")] = wifiLock;
ota[F("aota")] = aOtaEnabled;

File f = WLED_FS.open(SET_F("/wsec.json"), "w");
File f = WLED_FS.open(FPSTR(s_wsec_json), "w");
if (f) serializeJson(root, f);
f.close();
releaseJSONBufferLock();
Expand Down
2 changes: 1 addition & 1 deletion wled00/remote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ static bool remoteJson(int button)
JsonObject fdo = pDoc->as<JsonObject>();
if (fdo.isNull()) {
// the received button does not exist
//if (!WLED_FS.exists(SET_F("/remote.json"))) errorFlag = ERR_FS_RMLOAD; //warn if file itself doesn't exist
//if (!WLED_FS.exists(F("/remote.json"))) errorFlag = ERR_FS_RMLOAD; //warn if file itself doesn't exist
releaseJSONBufferLock();
return parsed;
}
Expand Down
8 changes: 4 additions & 4 deletions wled00/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,12 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
uint16_t freq = request->arg(sp).toInt();
if (IS_PWM(type)) {
switch (freq) {
case 0 : freq = WLED_PWM_FREQ/3; break;
case 1 : freq = WLED_PWM_FREQ/2; break;
case 0 : freq = WLED_PWM_FREQ/2; break;
case 1 : freq = WLED_PWM_FREQ*2/3; break;
default:
case 2 : freq = WLED_PWM_FREQ; break;
case 3 : freq = WLED_PWM_FREQ*4/3; break;
case 4 : freq = WLED_PWM_FREQ*2; break;
case 3 : freq = WLED_PWM_FREQ*2; break;
case 4 : freq = WLED_PWM_FREQ*4; break;
}
} else if (IS_DIGITAL(type) && IS_2PIN(type)) {
switch (freq) {
Expand Down
8 changes: 4 additions & 4 deletions wled00/xml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,12 @@ void getSettingsJS(byte subPage, char* dest)
uint16_t speed = bus->getFrequency();
if (IS_PWM(bus->getType())) {
switch (speed) {
case WLED_PWM_FREQ/3 : speed = 0; break;
case WLED_PWM_FREQ/2 : speed = 1; break;
case WLED_PWM_FREQ/2 : speed = 0; break;
case WLED_PWM_FREQ*2/3 : speed = 1; break;
default:
case WLED_PWM_FREQ : speed = 2; break;
case WLED_PWM_FREQ*4/3 : speed = 3; break;
case WLED_PWM_FREQ*2 : speed = 4; break;
case WLED_PWM_FREQ*2 : speed = 3; break;
case WLED_PWM_FREQ*4 : speed = 4; break;
}
} else if (IS_DIGITAL(bus->getType()) && IS_2PIN(bus->getType())) {
switch (speed) {
Expand Down

0 comments on commit c0beb62

Please sign in to comment.