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
This commit is contained in:
@@ -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() {
|
||||
if (display && _valid) display->stopDMAoutput(); // terminate DMA driver (display goes black)
|
||||
_valid = false;
|
||||
_panelWidth = 0;
|
||||
deallocatePins();
|
||||
USER_PRINTLN("HUB75 output ended.");
|
||||
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)
|
||||
_panelWidth = 0;
|
||||
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(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(mxconfig.gpio.lat, PinOwner::HUB75);
|
||||
pinManager.deallocatePin(mxconfig.gpio.oe, PinOwner::HUB75);
|
||||
pinManager.deallocatePin(mxconfig.gpio.clk, 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(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.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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user