From b810dca9d18c7074adbe9a088c97b5e0e2a0649e Mon Sep 17 00:00:00 2001 From: mrfaptastic <12006953+mrfaptastic@users.noreply.github.com> Date: Mon, 22 Nov 2021 07:43:07 +0000 Subject: [PATCH] 1/8 Scan Panel Example #154 #204 Also supports chaining. --- ESP32-HUB75-MatrixPanel-I2S-DMA.cpp | 10 +- ESP32-HUB75-MatrixPanel-I2S-DMA.h | 2 +- ESP32-VirtualMatrixPanel-I2S-DMA.h | 104 +++--- .../One_Eight_1_8_ScanPanel/1_8_ScanPanel.h | 69 ++++ .../One_Eight_1_8_ScanPanel.ino | 147 +++++++++ .../README.md | 6 +- .../OneEighthScanMatrixPanel.h | 296 ------------------ .../One_Eighth_1_8_ScanPanel.ino | 125 -------- 8 files changed, 283 insertions(+), 476 deletions(-) create mode 100644 examples/One_Eight_1_8_ScanPanel/1_8_ScanPanel.h create mode 100644 examples/One_Eight_1_8_ScanPanel/One_Eight_1_8_ScanPanel.ino rename examples/{One_Eighth_1_8_ScanPanel => One_Eight_1_8_ScanPanel}/README.md (69%) delete mode 100644 examples/One_Eighth_1_8_ScanPanel/OneEighthScanMatrixPanel.h delete mode 100644 examples/One_Eighth_1_8_ScanPanel/One_Eighth_1_8_ScanPanel.ino diff --git a/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp b/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp index c2d2b36..a82b37f 100644 --- a/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp +++ b/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp @@ -258,8 +258,9 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory() // Do a final check to see if we have enough space for the additional DMA linked list descriptors that will be required to link it all up! if(_dma_linked_list_memory_required > heap_caps_get_largest_free_block(MALLOC_CAP_DMA)) { +#if SERIAL_DEBUG Serial.println(F("ERROR: Not enough SRAM left over for DMA linked-list descriptor memory reservation! Oh so close!\r\n")); - +#endif return false; } // linked list descriptors memory check @@ -270,7 +271,9 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory() dmadesc_a = (lldesc_t *)heap_caps_malloc(desccount * sizeof(lldesc_t), MALLOC_CAP_DMA); assert("Can't allocate descriptor framebuffer a"); if(!dmadesc_a) { +#if SERIAL_DEBUG Serial.println(F("ERROR: Could not malloc descriptor framebuffer a.")); +#endif return false; } @@ -280,18 +283,21 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory() dmadesc_b = (lldesc_t *)heap_caps_malloc(desccount * sizeof(lldesc_t), MALLOC_CAP_DMA); assert("Could not malloc descriptor framebuffer b."); if(!dmadesc_b) { +#if SERIAL_DEBUG Serial.println(F("ERROR: Could not malloc descriptor framebuffer b.")); +#endif return false; } } +#if SERIAL_DEBUG Serial.println(F("*** ESP32-HUB75-MatrixPanel-I2S-DMA: Memory Allocations Complete ***")); Serial.printf_P(PSTR("Total memory that was reserved: %d kB.\r\n"), _total_dma_capable_memory_reserved/1024); Serial.printf_P(PSTR("... of which was used for the DMA Linked List(s): %d kB.\r\n"), _dma_linked_list_memory_required/1024); Serial.printf_P(PSTR("Heap Memory Available: %d bytes total. Largest free block: %d bytes.\r\n"), heap_caps_get_free_size(0), heap_caps_get_largest_free_block(0)); Serial.printf_P(PSTR("General RAM Available: %d bytes total. Largest free block: %d bytes.\r\n"), heap_caps_get_free_size(MALLOC_CAP_DEFAULT), heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT)); - +#endif // Just os we know initialized = true; diff --git a/ESP32-HUB75-MatrixPanel-I2S-DMA.h b/ESP32-HUB75-MatrixPanel-I2S-DMA.h index 9fb2229..4deeaac 100644 --- a/ESP32-HUB75-MatrixPanel-I2S-DMA.h +++ b/ESP32-HUB75-MatrixPanel-I2S-DMA.h @@ -315,7 +315,7 @@ struct HUB75_I2S_CFG { LAT_PIN_DEFAULT, OE_PIN_DEFAULT, CLK_PIN_DEFAULT }, shift_driver _drv = SHIFTREG, bool _dbuff = false, - clk_speed _i2sspeed = HZ_10M, + clk_speed _i2sspeed = HZ_20M, uint8_t _latblk = 1, // Anything > 1 seems to cause artefacts on ICS panels bool _clockphase = true, uint8_t _min_refresh_rate = 85 diff --git a/ESP32-VirtualMatrixPanel-I2S-DMA.h b/ESP32-VirtualMatrixPanel-I2S-DMA.h index 5e4ea71..80c119e 100644 --- a/ESP32-VirtualMatrixPanel-I2S-DMA.h +++ b/ESP32-VirtualMatrixPanel-I2S-DMA.h @@ -15,8 +15,15 @@ #endif struct VirtualCoords { + int16_t x; int16_t y; + int16_t virt_row; // chain of panels row + int16_t virt_col; // chain of panels col + + VirtualCoords() : x(0), y(0) + { } + }; @@ -38,6 +45,8 @@ class VirtualMatrixPanel int16_t panelResX; int16_t panelResY; + + int16_t dmaResX; // The width of the chain in pixels (as the DMA engine sees it) MatrixPanel_I2S_DMA *display; @@ -57,7 +66,11 @@ class VirtualMatrixPanel vmodule_cols = _vmodule_cols; virtualResX = vmodule_cols*_panelResX; - virtualResY = vmodule_rows*_panelResY; + virtualResY = vmodule_rows*_panelResY; + + dmaResX = panelResX * vmodule_rows * vmodule_cols; + + /* Virtual Display width() and height() will return a real-world value. For example: * Virtual Display width: 128 @@ -66,7 +79,6 @@ class VirtualMatrixPanel * So, not values that at 0 to X-1 */ - _s_chain_party = serpentine_chain; // serpentine, or 'S' chain? _chain_top_down= top_down_chain; @@ -74,39 +86,25 @@ class VirtualMatrixPanel } - VirtualCoords getCoords(int16_t x, int16_t y); - // equivalent methods of the matrix library so it can be just swapped out. virtual void drawPixel(int16_t x, int16_t y, uint16_t color); virtual void fillScreen(uint16_t color); // overwrite adafruit implementation - void clearScreen() { - display->clearScreen(); - } - //void drawPixelRGB565(int16_t x, int16_t y, uint16_t color); + void clearScreen() { display->clearScreen(); } void drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b); - //void drawPixelRGB24(int16_t x, int16_t y, RGB24 color); - void drawIcon (int *ico, int16_t x, int16_t y, int16_t icon_cols, int16_t icon_rows); - uint16_t color444(uint8_t r, uint8_t g, uint8_t b) { - return display->color444(r, g, b); - } - uint16_t color565(uint8_t r, uint8_t g, uint8_t b) { - return display->color565(r, g, b); - } - uint16_t color333(uint8_t r, uint8_t g, uint8_t b) { - return display->color333(r, g, b); - } + uint16_t color444(uint8_t r, uint8_t g, uint8_t b) { return display->color444(r, g, b); } + uint16_t color565(uint8_t r, uint8_t g, uint8_t b) { return display->color565(r, g, b); } + uint16_t color333(uint8_t r, uint8_t g, uint8_t b) { return display->color333(r, g, b); } void flipDMABuffer() { display->flipDMABuffer(); } - //void showDMABuffer() { display->showDMABuffer(); } + void drawDisplayTest(); + void setRotate(bool rotate); - void drawDisplayTest(); - - // Rotate display - inline void setRotate(bool rotate); - - private: + protected: + + virtual VirtualCoords getCoords(int16_t &x, int16_t &y); VirtualCoords coords; + bool _s_chain_party = true; // Are we chained? Ain't no party like a... bool _chain_top_down = false; // is the ESP at the top or bottom of the matrix of devices? bool _rotate = false; @@ -118,21 +116,29 @@ class VirtualMatrixPanel * Updates the private class member variable 'coords', so no need to use the return value. * Not thread safe, but not a concern for ESP32 sketch anyway... I think. */ -inline VirtualCoords VirtualMatrixPanel::getCoords(int16_t x, int16_t y) { +inline VirtualCoords VirtualMatrixPanel::getCoords(int16_t &x, int16_t &y) { + //Serial.println("Called Base."); + coords.x = coords.y = -1; // By defalt use an invalid co-ordinates that will be rejected by updateMatrixDMABuffer - coords.x = coords.y = -1; // By defalt use an invalid co-ordinates that will be rejected by updateMatrixDMABuffer + if ( x < 0 || x >= width() || y < 0 || y >= height() ) { // Co-ordinates go from 0 to X-1 remember! width() and height() are out of range! + //Serial.printf("VirtualMatrixPanel::getCoords(): Invalid virtual display coordinate. x,y: %d, %d\r\n", x, y); + return coords; + } - if (x < 0 || x >= width() || y < 0 || y >= height() ) { // Co-ordinates go from 0 to X-1 remember! width() and height() are out of range! - //Serial.printf("VirtualMatrixPanel::getCoords(): Invalid virtual display coordinate. x,y: %d, %d\r\n", x, y); - return coords; - } - // We want to rotate? if (_rotate){ uint16_t temp_x=x; x=y; y=virtualResY-1-temp_x; - } + } + + // Stupidity check + if ( vmodule_rows == vmodule_cols == 1) // single panel... + { + coords.x = x; + coords.y = y; + return coords; + } uint8_t row = (y / panelResY) + 1; //a non indexed 0 row number if( ( _s_chain_party && !_chain_top_down && (row % 2 == 0) ) // serpentine vertically stacked chain starting from bottom row (i.e. ESP closest to ground), upwards @@ -157,34 +163,29 @@ inline VirtualCoords VirtualMatrixPanel::getCoords(int16_t x, int16_t y) { // Reverse co-ordinates if panel chain from ESP starts from the TOP RIGHT if (_chain_top_down) { - const HUB75_I2S_CFG _cfg = this->display->getCfg(); - coords.x = (_cfg.mx_width * _cfg.chain_length - 1) - coords.x; - coords.y = (_cfg.mx_height-1) - coords.y; - + /* + const HUB75_I2S_CFG _cfg = this->display->getCfg(); + coords.x = (_cfg.mx_width * _cfg.chain_length - 1) - coords.x; + coords.y = (_cfg.mx_height-1) - coords.y; + */ + coords.x = (dmaResX - 1) - coords.x; + coords.y = (panelResY-1) - coords.y; } - //Serial.print("Mapping to x: "); Serial.print(coords.x, DEC); Serial.print(", y: "); Serial.println(coords.y, DEC); - return coords; - + return coords; } -inline void VirtualMatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t color) -{ - //VirtualCoords coords = getCoords(x, y); +inline void VirtualMatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t color) { // adafruit virtual void override getCoords(x, y); this->display->drawPixel(coords.x, coords.y, color); } -inline void VirtualMatrixPanel::fillScreen(uint16_t color) // adafruit virtual void override -{ - // No need to map this. +inline void VirtualMatrixPanel::fillScreen(uint16_t color) { // adafruit virtual void override this->display->fillScreen(color); } -inline void VirtualMatrixPanel::drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b) -{ - //VirtualCoords coords = getCoords(x, y); +inline void VirtualMatrixPanel::drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b) { getCoords(x, y); this->display->drawPixelRGB888( coords.x, coords.y, r, g, b); } @@ -213,7 +214,7 @@ inline void VirtualMatrixPanel::drawDisplayTest() } #endif - +/* // need to recreate this one, as it wouldn't work to just map where it starts. inline void VirtualMatrixPanel::drawIcon (int *ico, int16_t x, int16_t y, int16_t icon_cols, int16_t icon_rows) { int i, j; @@ -226,5 +227,6 @@ inline void VirtualMatrixPanel::drawIcon (int *ico, int16_t x, int16_t y, int16_ } } } +*/ #endif diff --git a/examples/One_Eight_1_8_ScanPanel/1_8_ScanPanel.h b/examples/One_Eight_1_8_ScanPanel/1_8_ScanPanel.h new file mode 100644 index 0000000..238b5a6 --- /dev/null +++ b/examples/One_Eight_1_8_ScanPanel/1_8_ScanPanel.h @@ -0,0 +1,69 @@ +#ifndef _ESP32_ONE_EIGHT_SCAN_PANEL +#define _ESP32_ONE_EIGHT_SCAN_PANEL + +#include + +/* This class inherits all the goodness of the VirtualMatrixPanel class used to created + large displays of chained panels, but does trickery to convert from 1/16 to 1/8 + DMA output. + + Some guidance given by looking at hzeller's code, in relation to the 'stretch factor' + concept where these panels are really double width and half the height, from the perspective + of the digital input required to get the 'real world' resolution and output. + https://github.com/hzeller/rpi-rgb-led-matrix/blob/master/lib/multiplex-mappers.cc +*/ +class OneEightScanPanel : public VirtualMatrixPanel +{ + public: + using VirtualMatrixPanel::VirtualMatrixPanel; // inherit VirtualMatrixPanel's constructor(s) + + protected: + /* Convert Real World 'VirtualMatrixPanel' co-ordinates (i.e. Real World pixel you're looking at + on the panel or chain of panels, per the chaining configuration) to a 1/8 panels + double 'stretched' and 'squished' coordinates which is what needs to be sent from the + DMA buffer. + + Note: Look at the One_Eight_1_8_ScanPanel code and you'll see that the DMA buffer is setup + as if the panel is 2 * W and 0.5 * H ! + */ + VirtualCoords getCoords(int16_t &x, int16_t &y); + +}; // end class header note the ; + + + +inline VirtualCoords OneEightScanPanel::getCoords(int16_t &x, int16_t &y) { + VirtualMatrixPanel::getCoords(x, y); // call to base to update coords for chaining approach + + if ( coords.x == -1 || coords.y == -1 ) { // Co-ordinates go from 0 to X-1 remember! width() and height() are out of range! + return coords; + } + +/* + Serial.print("VirtualMatrixPanel Mapping ("); Serial.print(x, DEC); Serial.print(","); Serial.print(y, DEC); Serial.print(") "); + // to + Serial.print("to ("); Serial.print(coords.x, DEC); Serial.print(","); Serial.print(coords.y, DEC); Serial.println(") "); + */ + if ( (y & 8) == 0) { + coords.x += ((coords.x / panelResX)+1)*panelResX; // 1st, 3rd 'block' of 8 rows of pixels, offset by panel width in DMA buffer + } + else { + coords.x += (coords.x / panelResX)*panelResX; // 2nd, 4th 'block' of 8 rows of pixels, offset by panel width in DMA buffer + } + + // http://cpp.sh/4ak5u + // Real number of DMA y rows is half reality + // coords.y = (y / 16)*8 + (y & 0b00000111); + coords.y = (y >> 4)*8 + (y & 0b00000111); + + /* + Serial.print("OneEightScanPanel Mapping ("); Serial.print(x, DEC); Serial.print(","); Serial.print(y, DEC); Serial.print(") "); + // to + Serial.print("to ("); Serial.print(coords.x, DEC); Serial.print(","); Serial.print(coords.y, DEC); Serial.println(") "); + */ + + return coords; +} + + +#endif diff --git a/examples/One_Eight_1_8_ScanPanel/One_Eight_1_8_ScanPanel.ino b/examples/One_Eight_1_8_ScanPanel/One_Eight_1_8_ScanPanel.ino new file mode 100644 index 0000000..65050aa --- /dev/null +++ b/examples/One_Eight_1_8_ScanPanel/One_Eight_1_8_ScanPanel.ino @@ -0,0 +1,147 @@ +/************************************************************************* + * Description: + * + * The underlying implementation of the ESP32-HUB75-MatrixPanel-I2S-DMA only + * supports output to 1/16 or 1/32 scan panels - which means outputting + * two lines at the same time, 16 or 32 rows apart. This cannot be changed + * at the DMA layer as it would require a messy and complex rebuild of the + * library's DMA internals. + * + * However, it is possible to connect 1/8 scan panels to this same library and + * 'trick' the output to work correctly on these panels by way of adjusting the + * pixel co-ordinates that are 'sent' to the ESP32-HUB75-MatrixPanel-I2S-DMA + * library. + * + **************************************************************************/ +#include "ESP32-HUB75-MatrixPanel-I2S-DMA.h" + +// Virtual Display to re-map co-ordinates such that they draw correctly on a 32x16 1/4 Scan panel +#include "1_8_ScanPanel.h" + + + // Panel configuration + #define PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module. + #define PANEL_RES_Y 32 // Number of pixels tall of each INDIVIDUAL panel module. + + + #define NUM_ROWS 1 // Number of rows of chained INDIVIDUAL PANELS + #define NUM_COLS 2 // Number of INDIVIDUAL PANELS per ROW + + // ^^^ NOTE: DEFAULT EXAMPLE SETUP IS FOR A CHAIN OF TWO x 1/8 SCAN PANELS + + // Change this to your needs, for details on VirtualPanel pls read the PDF! + #define SERPENT true + #define TOPDOWN false + + // placeholder for the matrix object + MatrixPanel_I2S_DMA *dma_display = nullptr; + + // placeholder for the virtual display object + OneEightScanPanel *OneEightMatrixDisplay = nullptr; + + /****************************************************************************** + * Setup! + ******************************************************************************/ + void setup() + { + delay(250); + + Serial.begin(115200); + Serial.println(""); Serial.println(""); Serial.println(""); + Serial.println("*****************************************************"); + Serial.println("* 1/8 Scan Panel Demonstration *"); + Serial.println("*****************************************************"); + +/* + // 62x32 1/8 Scan Panels don't have a D and E pin! + + HUB75_I2S_CFG::i2s_pins _pins = { + R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, + A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, + LAT_PIN, OE_PIN, CLK_PIN + }; +*/ + HUB75_I2S_CFG mxconfig( + PANEL_RES_X*2, // DO NOT CHANGE THIS + PANEL_RES_Y/2, // DO NOT CHANGE THIS + NUM_ROWS*NUM_COLS // DO NOT CHANGE THIS + //,_pins // Uncomment to enable custom pins + ); + + mxconfig.clkphase = false; // Change this if you see pixels showing up shifted wrongly by one column the left or right. + + //mxconfig.driver = HUB75_I2S_CFG::FM6126A; // in case that we use panels based on FM6126A chip, we can set it here before creating MatrixPanel_I2S_DMA object + + // OK, now we can create our matrix object + dma_display = new MatrixPanel_I2S_DMA(mxconfig); + + // let's adjust default brightness to about 75% + dma_display->setBrightness8(96); // range is 0-255, 0 - 0%, 255 - 100% + + // Allocate memory and start DMA display + if( not dma_display->begin() ) + Serial.println("****** !KABOOM! I2S memory allocation failed ***********"); + + + dma_display->clearScreen(); + delay(500); + + // create OneEightMatrixDisplaylay object based on our newly created dma_display object + OneEightMatrixDisplay = new OneEightScanPanel((*dma_display), NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, SERPENT, TOPDOWN); + + } + + + void loop() { + + // What the panel sees from the DMA engine! + for (int i=PANEL_RES_X*2+10; i< PANEL_RES_X*(NUM_ROWS*NUM_COLS)*2; i++) + { + dma_display->drawLine(i, 0, i, 7, dma_display->color565(255, 0, 0)); // red + delay(10); + } + + dma_display->clearScreen(); + delay(1000); +/* + // Try again using the pixel / dma memory remapper + for (int i=PANEL_RES_X+5; i< (PANEL_RES_X*2)-1; i++) + { + OneEightMatrixDisplay->drawLine(i, 0, i, 7, dma_display->color565(0, 0, 255)); // blue + delay(10); + } +*/ + + // Try again using the pixel / dma memory remapper + int offset = PANEL_RES_X*((NUM_ROWS*NUM_COLS)-1); + for (int i=0; i< PANEL_RES_X; i++) + { + OneEightMatrixDisplay->drawLine(i+offset, 0, i+offset, 7, dma_display->color565(0, 0, 255)); // blue + OneEightMatrixDisplay->drawLine(i+offset, 8, i+offset, 15, dma_display->color565(0, 128,0)); // g + OneEightMatrixDisplay->drawLine(i+offset, 16, i+offset, 23, dma_display->color565(128, 0,0)); // red + OneEightMatrixDisplay->drawLine(i+offset, 24, i+offset, 31, dma_display->color565(0, 128, 128)); // blue + delay(10); + } + + delay(1000); + + + // Print on each chained panel 1/8 module! + // This only really works for a single horizontal chain + for (int i = 0; i < NUM_ROWS*NUM_COLS; i++) + { + OneEightMatrixDisplay->setTextColor(OneEightMatrixDisplay->color565(255, 255, 255)); + OneEightMatrixDisplay->setCursor(i*PANEL_RES_X+7, OneEightMatrixDisplay->height()/3); + + // Red text inside red rect (2 pix in from edge) + OneEightMatrixDisplay->print("Panel " + String(i+1)); + OneEightMatrixDisplay->drawRect(1,1, OneEightMatrixDisplay->width()-2, OneEightMatrixDisplay->height()-2, OneEightMatrixDisplay->color565(255,0,0)); + + // White line from top left to bottom right + OneEightMatrixDisplay->drawLine(0,0, OneEightMatrixDisplay->width()-1, OneEightMatrixDisplay->height()-1, OneEightMatrixDisplay->color565(255,255,255)); + } + + delay(2000); + dma_display->clearScreen(); + + } // end loop diff --git a/examples/One_Eighth_1_8_ScanPanel/README.md b/examples/One_Eight_1_8_ScanPanel/README.md similarity index 69% rename from examples/One_Eighth_1_8_ScanPanel/README.md rename to examples/One_Eight_1_8_ScanPanel/README.md index 5920f65..4147bcb 100644 --- a/examples/One_Eighth_1_8_ScanPanel/README.md +++ b/examples/One_Eight_1_8_ScanPanel/README.md @@ -1,4 +1,4 @@ -# Using this library with 32x16 1/4 Scan Panels +# Using this library with 32x16 1/8 Scan Panels ## Problem ESP32-HUB75-MatrixPanel-I2S-DMA library will not display output correctly with 1/8 scan panels such [as this](https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/154) by default. @@ -7,3 +7,7 @@ ESP32-HUB75-MatrixPanel-I2S-DMA library will not display output correctly with 1 It is possible to connect 1/8 scan panels to this library and 'trick' the output to work correctly on these panels by way of adjusting the pixel co-ordinates that are 'sent' to the ESP32-HUB75-MatrixPanel-I2S-DMA library (in this example, it is the 'dmaOutput' class). Creation of a 'OneEighthScanMatrixPanel.h' class which sends an adjusted drawPixel() x,y co-ordinates to the underlying ESP32-HUB75-MatrixPanel-I2S-DMA library's drawPixel routine, to trick the output to look pixel perfect. + +Refer to the '1_8_ScanPanel.h' logic which builds upon the library's core 'Virtual Display' (ESP32-VirtualMatrixPanel-I2S-DMA.h) to also support chaining of 1/8 Scan Panels. Refer to 'ChainedPanels' example on how to configure panel chaining to create bigger displays. + + diff --git a/examples/One_Eighth_1_8_ScanPanel/OneEighthScanMatrixPanel.h b/examples/One_Eighth_1_8_ScanPanel/OneEighthScanMatrixPanel.h deleted file mode 100644 index 124236e..0000000 --- a/examples/One_Eighth_1_8_ScanPanel/OneEighthScanMatrixPanel.h +++ /dev/null @@ -1,296 +0,0 @@ -#ifndef _ESP32_ONE_EIGTH_MATRIX_PANEL_I2S_DMA -#define _ESP32_ONE_EIGTH_MATRIX_PANEL_I2S_DMA - -#include "ESP32-HUB75-MatrixPanel-I2S-DMA.h" - -struct VirtualCoords { - int16_t x; - int16_t y; -}; - - -#ifdef USE_GFX_ROOT -class OneEighthMatrixPanel : public GFX -#elif !defined NO_GFX -class OneEighthMatrixPanel : public Adafruit_GFX -#else -class OneEighthMatrixPanel -#endif -{ - - public: - int16_t virtualResX; - int16_t virtualResY; - - int16_t vmodule_rows; - int16_t vmodule_cols; - - int16_t panelResX; - int16_t panelResY; - - MatrixPanel_I2S_DMA *display; - - OneEighthMatrixPanel(MatrixPanel_I2S_DMA &disp, int _vmodule_rows, int _vmodule_cols, int _panelResX, int _panelResY, bool serpentine_chain = true, bool top_down_chain = false) -#ifdef USE_GFX_ROOT - : GFX(_vmodule_cols*_panelResX, _vmodule_rows*_panelResY) -#elif !defined NO_GFX - : Adafruit_GFX(_vmodule_cols*_panelResX, _vmodule_rows*_panelResY) -#endif - { - this->display = &disp; - - panelResX = _panelResX; - panelResY = _panelResY; - - vmodule_rows = _vmodule_rows; - vmodule_cols = _vmodule_cols; - - virtualResX = vmodule_cols*_panelResX; - virtualResY = vmodule_rows*_panelResY; - - /* Virtual Display width() and height() will return a real-world value. For example: - * Virtual Display width: 128 - * Virtual Display height: 64 - * - * So, not values that at 0 to X-1 - */ - _s_chain_party = serpentine_chain; // serpentine, or 'S' chain? - _chain_top_down= top_down_chain; - - coords.x = coords.y = -1; // By default use an invalid co-ordinates that will be rejected by updateMatrixDMABuffer - - } - - VirtualCoords getCoords(int16_t x, int16_t y); - - // equivalent methods of the matrix library so it can be just swapped out. - virtual void drawPixel(int16_t x, int16_t y, uint16_t color); - virtual void fillScreen(uint16_t color); // overwrite adafruit implementation - void clearScreen() { - display->clearScreen(); - } - void drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b); - void drawIcon (int *ico, int16_t x, int16_t y, int16_t icon_cols, int16_t icon_rows); - - uint16_t color444(uint8_t r, uint8_t g, uint8_t b) { - return display->color444(r, g, b); - } - uint16_t color565(uint8_t r, uint8_t g, uint8_t b) { - return display->color565(r, g, b); - } - uint16_t color333(uint8_t r, uint8_t g, uint8_t b) { - return display->color333(r, g, b); - } - - void flipDMABuffer() { display->flipDMABuffer(); } - - // Rotate display - inline void setRotate(bool rotate); - - private: - VirtualCoords coords; - bool _s_chain_party = true; // Are we chained? Ain't no party like a... - bool _chain_top_down = false; // is the ESP at the top or bottom of the matrix of devices? - bool _rotate = false; - -}; // end Class header - -/** - * Calculate virtual->real co-ordinate mapping to underlying single chain of panels connected to ESP32. - * Then do further calculations for 1/8 scan panel. - * Updates the private class member variable 'coords', so no need to use the return value. - * Not thread safe, but not a concern for ESP32 sketch anyway... I think. - */ -inline VirtualCoords OneEighthMatrixPanel::getCoords(int16_t x, int16_t y) { - - coords.x = coords.y = -1; // By default use an invalid co-ordinates that will be rejected by updateMatrixDMABuffer - - // Check if virtual work co-ordinates are outside the virtual display resolution space. This does NOT check - // against the physical real-world DMA matrix resolution / setup configured, that is used to actually output - // the electrical pulse to the panel. - - if (x < 0 || x >= width() || y < 0 || y >= height() ) { // Co-ordinates go from 0 to X-1 remember! width() and height() are out of range! - //Serial.printf("OneEighthMatrixPanel::getCoords(): Invalid virtual display coordinate. x,y: %d, %d\r\n", x, y); - return coords; - } - - // We want to rotate? - if (_rotate){ - uint16_t temp_x=x; - x=y; - y=virtualResY-1-temp_x; - } - - uint8_t row = (y / panelResY) + 1; //a non indexed 0 row number - //uint8_t col = (x / panelResX) + 1; //a non indexed 0 row number - if( ( _s_chain_party && !_chain_top_down && (row % 2 == 0) ) // serpentine vertically stacked chain starting from bottom row (i.e. ESP closest to ground), upwards - || - ( _s_chain_party && _chain_top_down && (row % 2 != 0) ) // serpentine vertically stacked chain starting from the sky downwards - ) - { - // First portion gets you to the correct offset for the row you need - // Second portion inverts the x on the row - coords.x = ((y / panelResY) * (virtualResX)) + (virtualResX - x) - 1; - - // inverts the y the row - coords.y = panelResY - 1 - (y % panelResY); - } - else - { - // Normal chain pixel co-ordinate - coords.x = x + ((y / panelResY) * (virtualResX)) ; - coords.y = y % panelResY; - } - - /* ******* - * START: 1/8 Scan Panel Pixel Re-Mapping - * - * We have calculated the x, y co-ordinates as if we have a chain of standard panels this library is designed - * for, this being 1/8 or 1/16 scan panels. We have to do some further hacking to convert co-ords to the - * double length and 1/2 physical dma output length that is required for these panels to work electronically. - */ - // https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/154 - // 1/8 Scan Panel - Is the final x-coord on the 1st or 3rd, 1/4ths (8 pixel 'blocks') of the panel (i.e. Row 0-7 or 17-24) ? - // Double the length of the x-coord if required - if ( ((coords.y /8) % 2) == 0) { // returns true/1 for the 1st and 3rd 8-pixel 1/4th of a 32px high panel - coords.x += (panelResX); - } - - // If virtual x-coord 'x' is on a panel n, then we need to start sending data from (panelResX)*2*n, given - // at the underlying DMA level these panels are actually 1/2 the height and double the length - coords.x += (panelResX)*2 * (panelResX)/x; - - - // Half the y coord. - coords.y = (y % 8); - if ( y >= panelResY/2 ) coords.y +=8; - - // Push all the pixels across a bit more if we're on another column or row - /* - uint8_t module_num = (row*col)-1; - if (module_num > 1) - { - //coords.x += ((panelResX)*2*(col*row))-1; - - } - */ - - - - /* - * END: 1/8 Scan Panel Pixel Re-Mapping - * ******* - */ - - // Reverse co-ordinates if panel chain from ESP starts from the TOP RIGHT - /* - if (_chain_top_down) - { - coords.x = (panelResX * vmodule_rows * vmodule_cols - 1) - coords.x; - coords.y = (panelResY-1) - coords.y; - - } - */ - - return coords; - -} - -inline void OneEighthMatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t color) -{ - //VirtualCoords coords = getCoords(x, y); - getCoords(x, y); - this->display->drawPixel(coords.x, coords.y, color); -} - -inline void OneEighthMatrixPanel::fillScreen(uint16_t color) // adafruit virtual void override -{ - // No need to map this. - this->display->fillScreen(color); -} - -inline void OneEighthMatrixPanel::drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b) -{ - //VirtualCoords coords = getCoords(x, y); - getCoords(x, y); - this->display->drawPixelRGB888( coords.x, coords.y, r, g, b); -} - -inline void OneEighthMatrixPanel::setRotate(bool rotate) { - _rotate=rotate; - - // We don't support rotation by degrees. - if (rotate) { setRotation(1); } else { setRotation(0); } -} - -// need to recreate this one, as it wouldn't work to just map where it starts. -inline void OneEighthMatrixPanel::drawIcon (int *ico, int16_t x, int16_t y, int16_t icon_cols, int16_t icon_rows) { - -} - -#endif - -/* -// http://cpp.sh/6skpy - - -// Example program -#include -#include - -int main() -{ - for (int i = 0; i < 32; i++) - { - int x = 0; - int y = i; - if ( ((y /8) % 2) == 0) { // returns true/1 for the 1st and 3rd 8-pixel 1/4th of a 32px high panel - x += 64; - } - - - y = (i % 8); - if ( i >= 32/2 ) y +=8; - - std::cout << "For input y = " << i << " mapping to y: " << y << " and x " << x << " \n"; - - } - - -} - - -For input y = 0 mapping to y: 0 and x 64 -For input y = 1 mapping to y: 1 and x 64 -For input y = 2 mapping to y: 2 and x 64 -For input y = 3 mapping to y: 3 and x 64 -For input y = 4 mapping to y: 4 and x 64 -For input y = 5 mapping to y: 5 and x 64 -For input y = 6 mapping to y: 6 and x 64 -For input y = 7 mapping to y: 7 and x 64 -For input y = 8 mapping to y: 0 and x 0 -For input y = 9 mapping to y: 1 and x 0 -For input y = 10 mapping to y: 2 and x 0 -For input y = 11 mapping to y: 3 and x 0 -For input y = 12 mapping to y: 4 and x 0 -For input y = 13 mapping to y: 5 and x 0 -For input y = 14 mapping to y: 6 and x 0 -For input y = 15 mapping to y: 7 and x 0 -For input y = 16 mapping to y: 8 and x 64 -For input y = 17 mapping to y: 9 and x 64 -For input y = 18 mapping to y: 10 and x 64 -For input y = 19 mapping to y: 11 and x 64 -For input y = 20 mapping to y: 12 and x 64 -For input y = 21 mapping to y: 13 and x 64 -For input y = 22 mapping to y: 14 and x 64 -For input y = 23 mapping to y: 15 and x 64 -For input y = 24 mapping to y: 8 and x 0 -For input y = 25 mapping to y: 9 and x 0 -For input y = 26 mapping to y: 10 and x 0 -For input y = 27 mapping to y: 11 and x 0 -For input y = 28 mapping to y: 12 and x 0 -For input y = 29 mapping to y: 13 and x 0 -For input y = 30 mapping to y: 14 and x 0 -For input y = 31 mapping to y: 15 and x 0 - -*/ \ No newline at end of file diff --git a/examples/One_Eighth_1_8_ScanPanel/One_Eighth_1_8_ScanPanel.ino b/examples/One_Eighth_1_8_ScanPanel/One_Eighth_1_8_ScanPanel.ino deleted file mode 100644 index f2def4e..0000000 --- a/examples/One_Eighth_1_8_ScanPanel/One_Eighth_1_8_ScanPanel.ino +++ /dev/null @@ -1,125 +0,0 @@ -/************************************************************************* - * Description: - * - * The underlying implementation of the ESP32-HUB75-MatrixPanel-I2S-DMA only - * supports output to 1/16 or 1/32 scan panels - which means outputting - * two lines at the same time, 16 or 32 rows apart. This cannot be changed - * at the DMA layer as it would require a messy and complex rebuild of the - * library's DMA internals. - * - * However, it is possible to connect 1/8 scan panels to this same library and - * 'trick' the output to work correctly on these panels by way of adjusting the - * pixel co-ordinates that are 'sent' to the ESP32-HUB75-MatrixPanel-I2S-DMA - * library (in this example, it is the 'dmaOutput' class). - * - * Supports chaining of multiple 1/8 panels... - * - **************************************************************************/ - - // Panel configuration - #define PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module. - #define PANEL_RES_Y 32 // Number of pixels tall of each INDIVIDUAL panel module. - - #define NUM_ROWS 1 // Number of rows of chained INDIVIDUAL PANELS - #define NUM_COLS 1 // Number of INDIVIDUAL PANELS per ROW - #define PANEL_CHAIN NUM_ROWS*NUM_COLS // total number of panels chained one to another - - // Change this to your needs, for details on VirtualPanel pls read the PDF! - #define SERPENT true - #define TOPDOWN false - - // GPIO Configuration - #define R1_PIN 2 - #define G1_PIN 15 - #define B1_PIN 4 - #define R2_PIN 16 - #define G2_PIN 27 - #define B2_PIN 17 - - #define A_PIN 5 - #define B_PIN 18 - #define C_PIN 19 - #define D_PIN -1 // Connected to GND on panel (21 if exist) - #define E_PIN -1 // Connected to GND on panel - - #define LAT_PIN 26 - #define OE_PIN 25 - #define CLK_PIN 22 - - #include "OneEighthScanMatrixPanel.h" // Virtual Display to re-map co-ordinates such that they draw correctly on a 32x16 1/4 Scan panel - - // placeholder for the matrix object - MatrixPanel_I2S_DMA *dma_display = nullptr; - - // placeholder for the virtual display object - OneEighthMatrixPanel *virtualDisp = nullptr; - - /****************************************************************************** - * Setup! - ******************************************************************************/ - void setup() { - - delay(2000); - Serial.begin(115200); - Serial.println(""); Serial.println(""); Serial.println(""); - Serial.println("*****************************************************"); - Serial.println("* 1/8 Scan Panel Demonstration *"); - Serial.println("*****************************************************"); - - - /****************************************************************************** - * Create physical DMA panel class AND virtual (chained) display class. - ******************************************************************************/ - - /* - The configuration for MatrixPanel_I2S_DMA object is held in HUB75_I2S_CFG structure, - All options has it's predefined default values. So we can create a new structure and redefine only the options we need - - Please refer to the '2_PatternPlasma.ino' example for detailed example of how to use the MatrixPanel_I2S_DMA configuration - */ - - HUB75_I2S_CFG::i2s_pins _pins = { - R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, - A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, - LAT_PIN, OE_PIN, CLK_PIN - }; - - - HUB75_I2S_CFG mxconfig( - PANEL_RES_X*2, // DO NOT CHANGE THIS - PANEL_RES_Y/2, // DO NOT CHANGE THIS - PANEL_CHAIN // DO NOT CHANGE THIS - //,_pins // Uncomment to enable custom pins - ); - - //mxconfig.driver = HUB75_I2S_CFG::FM6126A; // in case that we use panels based on FM6126A chip, we can set it here before creating MatrixPanel_I2S_DMA object - - // OK, now we can create our matrix object - dma_display = new MatrixPanel_I2S_DMA(mxconfig); - - // let's adjust default brightness to about 75% - dma_display->setBrightness8(192); // range is 0-255, 0 - 0%, 255 - 100% - - // Allocate memory and start DMA display - if( not dma_display->begin() ) - Serial.println("****** !KABOOM! I2S memory allocation failed ***********"); - - // create VirtualDisplay object based on our newly created dma_display object - virtualDisp = new OneEighthMatrixPanel((*dma_display), NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, SERPENT, TOPDOWN); - - virtualDisp->setTextColor(virtualDisp->color565(0, 0, 255)); - virtualDisp->setCursor(0, virtualDisp->height()/2); - - // Red text inside red rect (2 pix in from edge) - virtualDisp->print(" 1234"); - virtualDisp->drawRect(1,1, virtualDisp->width()-2, virtualDisp->height()-2, virtualDisp->color565(255,0,0)); - - // White line from top left to bottom right - virtualDisp->drawLine(0,0, virtualDisp->width()-1, virtualDisp->height()-1, virtualDisp->color565(255,255,255)); - } - - - void loop() { - - - } // end loop \ No newline at end of file