From 7ef84cfbfebfa8b0b291d261c2dc4a0870f0e5a1 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Mon, 26 Feb 2024 19:29:40 +0000 Subject: [PATCH 01/32] Add HUB75 support --- platformio.ini | 15 +++ wled00/bus_manager.cpp | 186 ++++++++++++++++++++++++++++++++++ wled00/bus_manager.h | 49 +++++++++ wled00/const.h | 3 + wled00/data/settings_leds.htm | 16 +++ wled00/pin_manager.h | 1 + wled00/xml.cpp | 4 + 7 files changed, 274 insertions(+) diff --git a/platformio.ini b/platformio.ini index 7ad66723ed..d8ead51043 100644 --- a/platformio.ini +++ b/platformio.ini @@ -461,3 +461,18 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME= -D HW_PIN_MISOSPI=9 ; -D STATUSLED=15 lib_deps = ${esp32s2.lib_deps} + + +[env:esp32dev_hub75] +board = esp32dev +upload_speed = 921600 +platform = ${esp32_idf_V4.platform} +platform_packages = ${esp32_idf_V4.platform_packages} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags_esp32_V4} -D WLED_RELEASE_NAME=ESP32_hub75 + -D WLED_ENABLE_HUB75MATRIX -D NO_GFX +lib_deps = ${esp32_idf_V4.lib_deps} + https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-DMA.git @ 3.0.10 + +monitor_filters = esp32_exception_decoder +board_build.partitions = ${esp32.default_partitions} diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 16701bbb78..ec731573f6 100755 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -605,6 +605,188 @@ void BusNetwork::cleanup() { freeData(); } +// *************************************************************************** + +#ifdef WLED_ENABLE_HUB75MATRIX + +BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { + + mxconfig.double_buff = false; // <------------- Turn on double buffer + + switch(bc.type) { + case 101: + mxconfig.mx_width = 32; + mxconfig.mx_height = 32; + break; + case 102: + mxconfig.mx_width = 64; + mxconfig.mx_height = 32; + break; + case 103: + mxconfig.mx_width = 64; + mxconfig.mx_height = 64; + break; + } + + mxconfig.chain_length = max((u_int8_t) 1, min(bc.pins[0], (u_int8_t) 4)); // prevent bad data preventing boot due to low memory + + if(mxconfig.mx_width >= 64 && (bc.pins[0] > 1)) { + DEBUG_PRINTF("WARNING, only single panel can be used of 64 pixel boards due to memory") + mxconfig.chain_length = 1; + } + + // mxconfig.driver = HUB75_I2S_CFG::SHIFTREG; + +#if defined(ARDUINO_ADAFRUIT_MATRIXPORTAL_ESP32S3) // MatrixPortal ESP32-S3 + + // https://www.adafruit.com/product/5778 + + DEBUG_PRINTF("MatrixPanel_I2S_DMA - Matrix Portal S3 config"); + + mxconfig.gpio.r1 = 42; + mxconfig.gpio.g1 = 41; + mxconfig.gpio.b1 = 40; + mxconfig.gpio.r2 = 38; + mxconfig.gpio.g2 = 39; + mxconfig.gpio.b2 = 37; + + mxconfig.gpio.lat = 47; + mxconfig.gpio.oe = 14; + mxconfig.gpio.clk = 2; + + mxconfig.gpio.a = 45; + mxconfig.gpio.b = 36; + mxconfig.gpio.c = 48; + mxconfig.gpio.d = 35; + mxconfig.gpio.e = 21; + +#else + +/* + + ESP32 with SmartMatrix's default pinout - ESP32_FORUM_PINOUT + + https://github.com/pixelmatix/SmartMatrix/blob/teensylc/src/MatrixHardware_ESP32_V0.h + + Can use a board like https://github.com/rorosaurus/esp32-hub75-driver + + #define R1_PIN GPIO_NUM_2 + #define G1_PIN GPIO_NUM_15 + #define B1_PIN GPIO_NUM_4 + #define R2_PIN GPIO_NUM_16 + #define G2_PIN GPIO_NUM_27 + #define B2_PIN GPIO_NUM_17 + + #define A_PIN GPIO_NUM_5 + #define B_PIN GPIO_NUM_18 + #define C_PIN GPIO_NUM_19 + #define D_PIN GPIO_NUM_21 + #define E_PIN GPIO_NUM_12 + #define LAT_PIN GPIO_NUM_26 + #define OE_PIN GPIO_NUM_25 + + #define CLK_PIN GPIO_NUM_22 +*/ + + DEBUG_PRINTF("MatrixPanel_I2S_DMA - ESP32 config"); + + mxconfig.gpio.r1 = 2; + mxconfig.gpio.g1 = 15; + mxconfig.gpio.b1 = 4; + mxconfig.gpio.r2 = 16; + mxconfig.gpio.g2 = 27; + mxconfig.gpio.b2 = 17; + + mxconfig.gpio.lat = 26; + mxconfig.gpio.oe = 25; + mxconfig.gpio.clk = 22; + + mxconfig.gpio.a = 5; + mxconfig.gpio.b = 18; + mxconfig.gpio.c = 19; + mxconfig.gpio.d = 21; + mxconfig.gpio.e = 12; + +#endif + + + DEBUG_PRINTF("MatrixPanel_I2S_DMA config - %ux%u length: %u\n", mxconfig.mx_width, mxconfig.mx_height, mxconfig.chain_length); + + // OK, now we can create our matrix object + display = new MatrixPanel_I2S_DMA(mxconfig); + + this->_len = (display->width() * display->height()); + + pinManager.allocatePin(mxconfig.gpio.r1, true, PinOwner::HUB75); + pinManager.allocatePin(mxconfig.gpio.g1, true, PinOwner::HUB75); + pinManager.allocatePin(mxconfig.gpio.b1, true, PinOwner::HUB75); + pinManager.allocatePin(mxconfig.gpio.r2, true, PinOwner::HUB75); + pinManager.allocatePin(mxconfig.gpio.g2, true, PinOwner::HUB75); + pinManager.allocatePin(mxconfig.gpio.b2, true, PinOwner::HUB75); + + pinManager.allocatePin(mxconfig.gpio.lat, true, PinOwner::HUB75); + pinManager.allocatePin(mxconfig.gpio.oe, true, PinOwner::HUB75); + pinManager.allocatePin(mxconfig.gpio.clk, true, PinOwner::HUB75); + + pinManager.allocatePin(mxconfig.gpio.a, true, PinOwner::HUB75); + pinManager.allocatePin(mxconfig.gpio.b, true, PinOwner::HUB75); + pinManager.allocatePin(mxconfig.gpio.c, true, PinOwner::HUB75); + pinManager.allocatePin(mxconfig.gpio.d, true, PinOwner::HUB75); + pinManager.allocatePin(mxconfig.gpio.e, true, PinOwner::HUB75); + + // display->setLatBlanking(4); + + DEBUG_PRINTF("MatrixPanel_I2S_DMA created"); + // let's adjust default brightness + display->setBrightness8(25); // range is 0-255, 0 - 0%, 255 - 100% + + // Allocate memory and start DMA display + if( not display->begin() ) { + DEBUG_PRINTF("****** MatrixPanel_I2S_DMA !KABOOM! I2S memory allocation failed ***********"); + return; + } + else { + _valid = true; + } + + DEBUG_PRINTF("MatrixPanel_I2S_DMA started"); +} + +void BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c) { + r = R(c); + g = G(c); + b = B(c); + x = pix % display->width(); + y = floor(pix / display->width()); + display->drawPixelRGB888(x, y, r, g, b); +} + +void BusHub75Matrix::setBrightness(uint8_t b, bool immediate) { + this->display->setBrightness(b); +} + +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); + +} +#endif +// *************************************************************************** //utility to get the approx. memory usage of a given BusConfig uint32_t BusManager::memUsage(BusConfig &bc) { @@ -637,6 +819,10 @@ int BusManager::add(BusConfig &bc) { if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1; if (IS_VIRTUAL(bc.type)) { busses[numBusses] = new BusNetwork(bc); +#ifdef WLED_ENABLE_HUB75MATRIX + } else if (bc.type >= TYPE_HUB75MATRIX && bc.type <= (TYPE_HUB75MATRIX + 10)) { + busses[numBusses] = new BusHub75Matrix(bc); +#endif } else if (IS_DIGITAL(bc.type)) { busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap); } else if (bc.type == TYPE_ONOFF) { diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 6dd9c39c8e..24f469b341 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -1,6 +1,9 @@ #ifndef BusManager_h #define BusManager_h +#ifdef WLED_ENABLE_HUB75MATRIX +#include +#endif /* * Class for addressing various light types */ @@ -56,6 +59,7 @@ struct BusConfig { if (type >= TYPE_NET_DDP_RGB && type < 96) nPins = 4; //virtual network bus. 4 "pins" store IP address else if (type > 47) nPins = 2; else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type); + else if (type >= TYPE_HUB75MATRIX && type <= (TYPE_HUB75MATRIX + 10)) nPins = 0; for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i]; } @@ -313,6 +317,51 @@ class BusNetwork : public Bus { bool _broadcastLock; }; +#ifdef WLED_ENABLE_HUB75MATRIX +class BusHub75Matrix : public Bus { + public: + BusHub75Matrix(BusConfig &bc); + + uint16_t getMaxPixels() override { return 4096; }; + + bool hasRGB() { return true; } + bool hasWhite() { return false; } + + void setPixelColor(uint16_t pix, uint32_t c); + + void show() { + if(mxconfig.double_buff) { + display->flipDMABuffer(); // Show the back buffer, set currently output buffer to the back (i.e. no longer being sent to LED panels) + display->clearScreen(); // Now clear the back-buffer + } + } + + void setBrightness(uint8_t b, bool immediate); + + uint8_t getPins(uint8_t* pinArray) { + pinArray[0] = mxconfig.chain_length; + return 1; + } // Fake value due to keep finaliseInit happy + + void deallocatePins(); + + void cleanup() { + deallocatePins(); + delete display; + _valid = false; + } + + ~BusHub75Matrix() { + cleanup(); + } + + private: + MatrixPanel_I2S_DMA *display = nullptr; + HUB75_I2S_CFG mxconfig; + uint8_t r, g, b, x, y; + +}; +#endif class BusManager { public: diff --git a/wled00/const.h b/wled00/const.h index da6fa85f60..af3e27a412 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -280,6 +280,9 @@ #define TYPE_LPD8806 52 #define TYPE_P9813 53 #define TYPE_LPD6803 54 + +#define TYPE_HUB75MATRIX 100 // 100 - 110 + //Network types (master broadcast) (80-95) #define TYPE_NET_DDP_RGB 80 //network DDP RGB bus (master broadcast bus) #define TYPE_NET_E131_RGB 81 //network E131 RGB bus (master broadcast bus, unused) diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 470d69d62e..796a5ef991 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -23,6 +23,19 @@ function isD2P(t) { return t > 47 && t < 64; } // is digital 2 pin type function is16b(t) { return t == 26 || t == 29 } // is digital 16 bit type function isVir(t) { return t >= 80 && t < 96; } // is virtual type + function hideHub75() { + var s = d.getElementsByTagName("select"); + for (i=0; i 0; j--) { + var t = parseInt(selectobject.options[j].value); + if(t >= 100 && t <= 110) selectobject.remove(j); + } + } + } + } // https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript function loadJS(FILE_URL, async = true) { let scE = d.createElement("script"); @@ -413,6 +426,9 @@ + + +
mA/LED: