From 3fa5d0c5ba5f8cb39a7e1c2506616af1869f7921 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 14 Oct 2024 13:05:27 +0200 Subject: [PATCH] HUB75 on S3: workaround for not working runtime reconfiguration * instead of deleting the driver object, we keep a "secret" reference and re-use it * prevent having more than one HUB75 object --- wled00/bus_manager.cpp | 156 ++++++++++++++++++++++++++++++++--------- wled00/bus_manager.h | 12 ++-- 2 files changed, 130 insertions(+), 38 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 1b59b50bf9..baf73cfe57 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -522,19 +522,35 @@ void BusNetwork::cleanup() { #ifdef WLED_ENABLE_HUB75MATRIX #warning "HUB75 driver enabled (experimental)" +// BusHub75Matrix "global" variables (static members) +MatrixPanel_I2S_DMA* BusHub75Matrix::activeDisplay = nullptr; +VirtualMatrixPanel* BusHub75Matrix::activeFourScanPanel = nullptr; +HUB75_I2S_CFG BusHub75Matrix::activeMXconfig = HUB75_I2S_CFG(); +uint8_t BusHub75Matrix::activeType = 0; +uint8_t BusHub75Matrix::instanceCount = 0; + BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { + MatrixPanel_I2S_DMA* display = nullptr; + VirtualMatrixPanel* fourScanPanel = nullptr; + HUB75_I2S_CFG mxconfig; size_t lastHeap = ESP.getFreeHeap(); _valid = false; - fourScanPanel = nullptr; _len = 0; + // allow exactly one instance + if (instanceCount > 0) { + USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! already active - preventing attempt to create more than one driver instance."); + return; + } + mxconfig.double_buff = false; // Use our own memory-optimised buffer rather than the driver's own double-buffer // mxconfig.driver = HUB75_I2S_CFG::ICN2038S; // experimental - use specific shift register driver // mxconfig.driver = HUB75_I2S_CFG::FM6124; // try this driver in case you panel stays dark, or when colors look too pastel - // mxconfig.latch_blanking = 3; + // mxconfig.latch_blanking = 1; // needed for some ICS panels + // mxconfig.latch_blanking = 3; // use in case you see gost images // mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M; // experimental - 5MHZ should be enugh, but colours looks slightly better at 10MHz // mxconfig.min_refresh_rate = 90; mxconfig.clkphase = false; // can help in case that the leftmost column is invisible, or pixels on the right side "bleeds out" to the left. @@ -745,11 +761,50 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh USER_PRINTF("MatrixPanel_I2S_DMA config - %ux%u (type %u) length: %u, %u bits/pixel.\n", mxconfig.mx_width, mxconfig.mx_height, bc.type, mxconfig.chain_length, mxconfig.getPixelColorDepthBits() * 3); DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(ESP.getFreeHeap()); lastHeap = ESP.getFreeHeap(); + // check if we can re-use the existing display driver + if (activeDisplay) { + if ( (memcmp(&(activeMXconfig.gpio), &(mxconfig.gpio), sizeof(mxconfig.gpio)) != 0) // other pins? + || (activeMXconfig.chain_length != mxconfig.chain_length) // other chain length? + || (activeMXconfig.mx_width != mxconfig.mx_width) || (activeMXconfig.mx_height != mxconfig.mx_height) // other size? + || (bc.type != activeType) // different panel type ? + || (activeMXconfig.clkphase != mxconfig.clkphase) // different driver options ? + || (activeMXconfig.latch_blanking != mxconfig.latch_blanking) + || (activeMXconfig.i2sspeed != mxconfig.i2sspeed) + || (activeMXconfig.driver != mxconfig.driver) + || (activeMXconfig.min_refresh_rate != mxconfig.min_refresh_rate) + || (activeMXconfig.getPixelColorDepthBits() != mxconfig.getPixelColorDepthBits()) ) + { + // not the same as before - delete old driver + DEBUG_PRINTLN("MatrixPanel_I2S_DMA deleting old driver!"); + activeDisplay->stopDMAoutput(); + delay(28); + //#if !defined(CONFIG_IDF_TARGET_ESP32S3) // prevent crash + delete activeDisplay; + //#endif + activeDisplay = nullptr; + activeFourScanPanel = nullptr; + #if defined(CONFIG_IDF_TARGET_ESP32S3) // runtime reconfiguration is not working on -S3 + USER_PRINTLN("\n\n****** MatrixPanel_I2S_DMA !KABOOM WARNING! Reboot needed to change driver options ***********\n"); + errorFlag = ERR_REBOOT_NEEDED; + #endif + } + } + // OK, now we can create our matrix object - display = new MatrixPanel_I2S_DMA(mxconfig); + bool newDisplay = false; // true when the previous display object wasn't re-used + if (!activeDisplay) { + display = new MatrixPanel_I2S_DMA(mxconfig); // create new matrix object + newDisplay = true; + } else { + display = activeDisplay; // continue with existing matrix object + fourScanPanel = activeFourScanPanel; + } + if (display == nullptr) { USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! driver allocation failed ***********"); - USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); + activeDisplay = nullptr; + activeFourScanPanel = nullptr; + USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap())); return; } @@ -776,21 +831,23 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh USER_PRINTLN("MatrixPanel_I2S_DMA created"); // let's adjust default brightness - display->setBrightness8(25); // range is 0-255, 0 - 0%, 255 - 100% + //display->setBrightness8(25); // range is 0-255, 0 - 0%, 255 - 100% // [setBrightness()] Tried to set output brightness before begin() _bri = 25; delay(24); // experimental - DEBUG_PRINT(F("heap usage: ")); DEBUG_PRINTLN(lastHeap - ESP.getFreeHeap()); + DEBUG_PRINT(F("heap usage: ")); DEBUG_PRINTLN(int(lastHeap - ESP.getFreeHeap())); // Allocate memory and start DMA display - if (display->begin() == false) { + if (newDisplay && (display->begin() == false)) { USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! I2S memory allocation failed ***********"); - USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); + USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap())); _valid = false; return; } else { - USER_PRINTLN("MatrixPanel_I2S_DMA begin ok"); - USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); + if (newDisplay) { USER_PRINTLN("MatrixPanel_I2S_DMA begin, started ok"); } + else { USER_PRINTLN("MatrixPanel_I2S_DMA begin, using existing display."); } + + USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap())); delay(18); // experiment - give the driver a moment (~ one full frame @ 60hz) to settle _valid = true; display->clearScreen(); // initially clear the screen buffer @@ -826,19 +883,19 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh switch(bc.type) { case 105: USER_PRINTLN("MatrixPanel_I2S_DMA FOUR_SCAN_32PX_HIGH - 32x32"); - fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, 32, 32); + if (!fourScanPanel) fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, 32, 32); fourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_32PX_HIGH); fourScanPanel->setRotation(0); break; case 106: USER_PRINTLN("MatrixPanel_I2S_DMA FOUR_SCAN_32PX_HIGH - 64x32"); - fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, 64, 32); + if (!fourScanPanel) fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, 64, 32); fourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_32PX_HIGH); fourScanPanel->setRotation(0); break; case 107: USER_PRINTLN("MatrixPanel_I2S_DMA FOUR_SCAN_64PX_HIGH"); - fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, 64, 64); + if (!fourScanPanel) fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, 64, 64); fourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_64PX_HIGH); fourScanPanel->setRotation(0); break; @@ -858,6 +915,15 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh USER_PRINT((_ledBuffer? _len*sizeof(CRGB) :0) + (_ledsDirty? getBitArrayBytes(_len) :0)); USER_PRINTLN(F(" bytes.")); } + + if (_valid) { + // config is active, copy to global + activeType = bc.type; + activeDisplay = display; + activeFourScanPanel = fourScanPanel; + if (newDisplay) memcpy(&activeMXconfig, &mxconfig, sizeof(mxconfig)); + } + instanceCount++; USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap())); } @@ -874,6 +940,8 @@ void __attribute__((hot)) BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c } else { // no double buffer allocated --> directly draw pixel + MatrixPanel_I2S_DMA* display = BusHub75Matrix::activeDisplay; + VirtualMatrixPanel* fourScanPanel = BusHub75Matrix::activeFourScanPanel; #ifndef NO_CIE1931 c = unGamma24(c); // to use the driver linear brightness feature, we first need to undo WLED gamma correction #endif @@ -914,16 +982,21 @@ uint32_t __attribute__((hot)) BusHub75Matrix::getPixelColorRestored(uint16_t pix void BusHub75Matrix::setBrightness(uint8_t b, bool immediate) { _bri = b; if (!_valid) return; + MatrixPanel_I2S_DMA* display = BusHub75Matrix::activeDisplay; // if (_bri > 238) _bri=238; // not strictly needed. Enable this line if you see glitches at highest brightness. - display->setBrightness(_bri); + if ((_bri > 253) && (activeMXconfig.latch_blanking < 2)) _bri=253; // prevent glitches at highest brightness. + if (display) display->setBrightness(_bri); } void __attribute__((hot)) BusHub75Matrix::show(void) { if (!_valid) return; + MatrixPanel_I2S_DMA* display = BusHub75Matrix::activeDisplay; + if (!display) return; display->setBrightness(_bri); if (_ledBuffer) { // write out buffered LEDs + VirtualMatrixPanel* fourScanPanel = BusHub75Matrix::activeFourScanPanel; bool isFourScan = (fourScanPanel != nullptr); //if (isFourScan) fourScanPanel->setRotation(0); unsigned height = isFourScan ? fourScanPanel->height() : display->height(); @@ -951,38 +1024,53 @@ void __attribute__((hot)) BusHub75Matrix::show(void) { } void BusHub75Matrix::cleanup() { + MatrixPanel_I2S_DMA* display = BusHub75Matrix::activeDisplay; + VirtualMatrixPanel* fourScanPanel = BusHub75Matrix::activeFourScanPanel; + if (display) display->clearScreen(); + +#if !defined(CONFIG_IDF_TARGET_ESP32S3) // S3: don't stop, as we want to re-use the driver later if (display && _valid) display->stopDMAoutput(); // terminate DMA driver (display goes black) - _valid = false; _panelWidth = 0; - deallocatePins(); USER_PRINTLN("HUB75 output ended."); +#else + USER_PRINTLN("HUB75 output paused."); +#endif + _valid = false; + deallocatePins(); //if (fourScanPanel != nullptr) delete fourScanPanel; // warning: deleting object of polymorphic class type 'VirtualMatrixPanel' which has non-virtual destructor might cause undefined behavior +#if !defined(CONFIG_IDF_TARGET_ESP32S3) // S3: don't delete, as we want to re-use the driver later if (display) delete display; - display = nullptr; - fourScanPanel = nullptr; + activeDisplay = nullptr; + activeFourScanPanel = nullptr; + USER_PRINTLN("HUB75 deleted."); +#else + USER_PRINTLN("HUB75 cleanup done."); +#endif + + if (instanceCount > 0) instanceCount--; if (_ledBuffer != nullptr) free(_ledBuffer); _ledBuffer = nullptr; if (_ledsDirty != nullptr) free(_ledsDirty); _ledsDirty = nullptr; } void BusHub75Matrix::deallocatePins() { - pinManager.deallocatePin(mxconfig.gpio.r1, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.g1, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.b1, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.r2, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.g2, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.b2, PinOwner::HUB75); - - pinManager.deallocatePin(mxconfig.gpio.lat, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.oe, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.clk, PinOwner::HUB75); - - pinManager.deallocatePin(mxconfig.gpio.a, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.b, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.c, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.d, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.e, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.r1, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.g1, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.b1, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.r2, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.g2, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.b2, PinOwner::HUB75); + + pinManager.deallocatePin(activeMXconfig.gpio.lat, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.oe, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.clk, PinOwner::HUB75); + + pinManager.deallocatePin(activeMXconfig.gpio.a, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.b, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.c, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.d, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.e, PinOwner::HUB75); } #endif diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 677e71de36..350a5f3f24 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -388,7 +388,7 @@ class BusHub75Matrix : public Bus { void setBrightness(uint8_t b, bool immediate) override; uint8_t getPins(uint8_t* pinArray) const override { - pinArray[0] = mxconfig.chain_length; + pinArray[0] = activeMXconfig.chain_length; return 1; } // Fake value due to keep finaliseInit happy @@ -401,12 +401,16 @@ class BusHub75Matrix : public Bus { } private: - MatrixPanel_I2S_DMA *display = nullptr; - VirtualMatrixPanel *fourScanPanel = nullptr; - HUB75_I2S_CFG mxconfig; unsigned _panelWidth = 0; CRGB *_ledBuffer = nullptr; byte *_ledsDirty = nullptr; + // C++ dirty trick: private static variables are actually _not_ part of the class (however only visibile to class instances). + // These variables persist when BusHub75Matrix gets deleted. + static MatrixPanel_I2S_DMA *activeDisplay; // active display object + static VirtualMatrixPanel *activeFourScanPanel; // active fourScan object + static HUB75_I2S_CFG activeMXconfig; // last used mxconfig + static uint8_t activeType; // last used type + static uint8_t instanceCount; // active instances - 0 or 1 }; #endif