From a6cd7eebb0b960c32228b18d93e9a7a4be1f5f26 Mon Sep 17 00:00:00 2001 From: user Date: Sun, 30 Mar 2025 16:11:48 +0200 Subject: Get Test patterns running on my 64x64 panel MBI5124GP https://blog.node5.net/Miscellaneous/64x64%20HUB%2075%20LED%20Matrix/ --- .../AnimatedGIFPanel_LittleFS.ino | 276 --------------------- examples/AnimatedGIFPanel_LittleFS/src/main.cpp | 276 +++++++++++++++++++++ examples/PIO_TestPatterns/platformio.ini | 4 +- examples/PIO_TestPatterns/src/main.cpp | 80 +++--- 4 files changed, 314 insertions(+), 322 deletions(-) delete mode 100644 examples/AnimatedGIFPanel_LittleFS/AnimatedGIFPanel_LittleFS.ino create mode 100644 examples/AnimatedGIFPanel_LittleFS/src/main.cpp (limited to 'examples') diff --git a/examples/AnimatedGIFPanel_LittleFS/AnimatedGIFPanel_LittleFS.ino b/examples/AnimatedGIFPanel_LittleFS/AnimatedGIFPanel_LittleFS.ino deleted file mode 100644 index f8637b0..0000000 --- a/examples/AnimatedGIFPanel_LittleFS/AnimatedGIFPanel_LittleFS.ino +++ /dev/null @@ -1,276 +0,0 @@ -// Example sketch which shows how to display a 64x32 animated GIF image stored in FLASH memory -// on a 64x32 LED matrix -// -// Credits: https://github.com/bitbank2/AnimatedGIF/tree/master/examples/ESP32_LEDMatrix_I2S -// - -/* INSTRUCTIONS - * - * 1. First Run the 'ESP32 Sketch Data Upload Tool' in Arduino from the 'Tools' Menu. - * - If you don't know what this is or see it as an option, then read this: - * https://github.com/me-no-dev/arduino-esp32fs-plugin - * - This tool will upload the contents of the data/ directory in the sketch folder onto - * the ESP32 itself. - * - * 2. You can drop any animated GIF you want in there, but keep it to the resolution of the - * MATRIX you're displaying to. To resize a gif, use this online website: https://ezgif.com/ - * - * 3. Have fun. - */ - -#include "FS.h" -#include -#include -#include - -#define FILESYSTEM LittleFS -#define FORMAT_LITTLEFS_IF_FAILED true - -#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 PANEL_CHAIN 1 // Total number of panels chained one to another horizontally only. - -//MatrixPanel_I2S_DMA dma_display; -MatrixPanel_I2S_DMA *dma_display = nullptr; - -uint16_t myBLACK = dma_display->color565(0, 0, 0); -uint16_t myWHITE = dma_display->color565(255, 255, 255); -uint16_t myRED = dma_display->color565(255, 0, 0); -uint16_t myGREEN = dma_display->color565(0, 255, 0); -uint16_t myBLUE = dma_display->color565(0, 0, 255); - -AnimatedGIF gif; -File f; -int x_offset, y_offset; - - - -// Draw a line of image directly on the LED Matrix -void GIFDraw(GIFDRAW *pDraw) -{ - uint8_t *s; - uint16_t *d, *usPalette, usTemp[320]; - int x, y, iWidth; - - iWidth = pDraw->iWidth; - if (iWidth > dma_display->width()) - iWidth = dma_display->width(); - - usPalette = pDraw->pPalette; - y = pDraw->iY + pDraw->y; // current line - - s = pDraw->pPixels; - if (pDraw->ucDisposalMethod == 2) // restore to background color - { - for (x=0; xucTransparent) - s[x] = pDraw->ucBackground; - } - pDraw->ucHasTransparency = 0; - } - // Apply the new pixels to the main image - if (pDraw->ucHasTransparency) // if transparency used - { - uint8_t *pEnd, c, ucTransparent = pDraw->ucTransparent; - int x, iCount; - pEnd = s + pDraw->iWidth; - x = 0; - iCount = 0; // count non-transparent pixels - while(x < pDraw->iWidth) - { - c = ucTransparent-1; - d = usTemp; - while (c != ucTransparent && s < pEnd) - { - c = *s++; - if (c == ucTransparent) // done, stop - { - s--; // back up to treat it like transparent - } - else // opaque - { - *d++ = usPalette[c]; - iCount++; - } - } // while looking for opaque pixels - if (iCount) // any opaque pixels? - { - for(int xOffset = 0; xOffset < iCount; xOffset++ ){ - dma_display->drawPixel(x + xOffset, y, usTemp[xOffset]); // 565 Color Format - } - x += iCount; - iCount = 0; - } - // no, look for a run of transparent pixels - c = ucTransparent; - while (c == ucTransparent && s < pEnd) - { - c = *s++; - if (c == ucTransparent) - iCount++; - else - s--; - } - if (iCount) - { - x += iCount; // skip these - iCount = 0; - } - } - } - else // does not have transparency - { - s = pDraw->pPixels; - // Translate the 8-bit pixels through the RGB565 palette (already byte reversed) - for (x=0; xiWidth; x++) - { - dma_display->drawPixel(x, y, usPalette[*s++]); // color 565 - } - } -} /* GIFDraw() */ - - -void * GIFOpenFile(const char *fname, int32_t *pSize) -{ - Serial.print("Playing gif: "); - Serial.println(fname); - f = FILESYSTEM.open(fname); - if (f) - { - *pSize = f.size(); - return (void *)&f; - } - return NULL; -} /* GIFOpenFile() */ - -void GIFCloseFile(void *pHandle) -{ - File *f = static_cast(pHandle); - if (f != NULL) - f->close(); -} /* GIFCloseFile() */ - -int32_t GIFReadFile(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen) -{ - int32_t iBytesRead; - iBytesRead = iLen; - File *f = static_cast(pFile->fHandle); - // Note: If you read a file all the way to the last byte, seek() stops working - if ((pFile->iSize - pFile->iPos) < iLen) - iBytesRead = pFile->iSize - pFile->iPos - 1; // <-- ugly work-around - if (iBytesRead <= 0) - return 0; - iBytesRead = (int32_t)f->read(pBuf, iBytesRead); - pFile->iPos = f->position(); - return iBytesRead; -} /* GIFReadFile() */ - -int32_t GIFSeekFile(GIFFILE *pFile, int32_t iPosition) -{ - int i = micros(); - File *f = static_cast(pFile->fHandle); - f->seek(iPosition); - pFile->iPos = (int32_t)f->position(); - i = micros() - i; -// Serial.printf("Seek time = %d us\n", i); - return pFile->iPos; -} /* GIFSeekFile() */ - -unsigned long start_tick = 0; - -void ShowGIF(char *name) -{ - start_tick = millis(); - - if (gif.open(name, GIFOpenFile, GIFCloseFile, GIFReadFile, GIFSeekFile, GIFDraw)) - { - x_offset = (dma_display->width() - gif.getCanvasWidth())/2; - if (x_offset < 0) x_offset = 0; - y_offset = (dma_display->height() - gif.getCanvasHeight())/2; - if (y_offset < 0) y_offset = 0; - Serial.printf("Successfully opened GIF; Canvas size = %d x %d\n", gif.getCanvasWidth(), gif.getCanvasHeight()); - Serial.flush(); - while (gif.playFrame(true, NULL)) - { - if ( (millis() - start_tick) > 8000) { // we'll get bored after about 8 seconds of the same looping gif - break; - } - } - gif.close(); - } - -} /* ShowGIF() */ - - - -/************************* Arduino Sketch Setup and Loop() *******************************/ -void setup() { - Serial.begin(115200); - - if(!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)){ - Serial.println("LittleFS Mount Failed"); - return; - } - - HUB75_I2S_CFG mxconfig( - PANEL_RES_X, // module width - PANEL_RES_Y, // module height - PANEL_CHAIN // Chain of panels - Horizontal width only. - ); - - // mxconfig.gpio.e = 18; - // mxconfig.clkphase = false; - // mxconfig.driver = HUB75_I2S_CFG::FM6126A; - - // Display Setup - dma_display = new MatrixPanel_I2S_DMA(mxconfig); - dma_display->begin(); - dma_display->setBrightness8(128); //0-255 - dma_display->clearScreen(); - dma_display->fillScreen(myWHITE); - - // Start going through GIFS - gif.begin(LITTLE_ENDIAN_PIXELS); - -} - -String gifDir = "/gifs"; // play all GIFs in this directory on the SD card -char filePath[256] = { 0 }; -File root, gifFile; - -void loop() -{ - while (1) // run forever - { - - root = FILESYSTEM.open(gifDir); - if (root) - { - gifFile = root.openNextFile(); - while (gifFile) - { - if (!gifFile.isDirectory()) // play it - { - - // C-strings... urghh... - memset(filePath, 0x0, sizeof(filePath)); - strcpy(filePath, gifFile.path()); - - // Show it. - ShowGIF(filePath); - - } - gifFile.close(); - gifFile = root.openNextFile(); - } - root.close(); - } // root - - delay(1000); // pause before restarting - - } // while -} - -// Other LittleFS filesystem function examples available at: -// https://randomnerdtutorials.com/esp32-write-data-littlefs-arduino/ \ No newline at end of file diff --git a/examples/AnimatedGIFPanel_LittleFS/src/main.cpp b/examples/AnimatedGIFPanel_LittleFS/src/main.cpp new file mode 100644 index 0000000..f8637b0 --- /dev/null +++ b/examples/AnimatedGIFPanel_LittleFS/src/main.cpp @@ -0,0 +1,276 @@ +// Example sketch which shows how to display a 64x32 animated GIF image stored in FLASH memory +// on a 64x32 LED matrix +// +// Credits: https://github.com/bitbank2/AnimatedGIF/tree/master/examples/ESP32_LEDMatrix_I2S +// + +/* INSTRUCTIONS + * + * 1. First Run the 'ESP32 Sketch Data Upload Tool' in Arduino from the 'Tools' Menu. + * - If you don't know what this is or see it as an option, then read this: + * https://github.com/me-no-dev/arduino-esp32fs-plugin + * - This tool will upload the contents of the data/ directory in the sketch folder onto + * the ESP32 itself. + * + * 2. You can drop any animated GIF you want in there, but keep it to the resolution of the + * MATRIX you're displaying to. To resize a gif, use this online website: https://ezgif.com/ + * + * 3. Have fun. + */ + +#include "FS.h" +#include +#include +#include + +#define FILESYSTEM LittleFS +#define FORMAT_LITTLEFS_IF_FAILED true + +#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 PANEL_CHAIN 1 // Total number of panels chained one to another horizontally only. + +//MatrixPanel_I2S_DMA dma_display; +MatrixPanel_I2S_DMA *dma_display = nullptr; + +uint16_t myBLACK = dma_display->color565(0, 0, 0); +uint16_t myWHITE = dma_display->color565(255, 255, 255); +uint16_t myRED = dma_display->color565(255, 0, 0); +uint16_t myGREEN = dma_display->color565(0, 255, 0); +uint16_t myBLUE = dma_display->color565(0, 0, 255); + +AnimatedGIF gif; +File f; +int x_offset, y_offset; + + + +// Draw a line of image directly on the LED Matrix +void GIFDraw(GIFDRAW *pDraw) +{ + uint8_t *s; + uint16_t *d, *usPalette, usTemp[320]; + int x, y, iWidth; + + iWidth = pDraw->iWidth; + if (iWidth > dma_display->width()) + iWidth = dma_display->width(); + + usPalette = pDraw->pPalette; + y = pDraw->iY + pDraw->y; // current line + + s = pDraw->pPixels; + if (pDraw->ucDisposalMethod == 2) // restore to background color + { + for (x=0; xucTransparent) + s[x] = pDraw->ucBackground; + } + pDraw->ucHasTransparency = 0; + } + // Apply the new pixels to the main image + if (pDraw->ucHasTransparency) // if transparency used + { + uint8_t *pEnd, c, ucTransparent = pDraw->ucTransparent; + int x, iCount; + pEnd = s + pDraw->iWidth; + x = 0; + iCount = 0; // count non-transparent pixels + while(x < pDraw->iWidth) + { + c = ucTransparent-1; + d = usTemp; + while (c != ucTransparent && s < pEnd) + { + c = *s++; + if (c == ucTransparent) // done, stop + { + s--; // back up to treat it like transparent + } + else // opaque + { + *d++ = usPalette[c]; + iCount++; + } + } // while looking for opaque pixels + if (iCount) // any opaque pixels? + { + for(int xOffset = 0; xOffset < iCount; xOffset++ ){ + dma_display->drawPixel(x + xOffset, y, usTemp[xOffset]); // 565 Color Format + } + x += iCount; + iCount = 0; + } + // no, look for a run of transparent pixels + c = ucTransparent; + while (c == ucTransparent && s < pEnd) + { + c = *s++; + if (c == ucTransparent) + iCount++; + else + s--; + } + if (iCount) + { + x += iCount; // skip these + iCount = 0; + } + } + } + else // does not have transparency + { + s = pDraw->pPixels; + // Translate the 8-bit pixels through the RGB565 palette (already byte reversed) + for (x=0; xiWidth; x++) + { + dma_display->drawPixel(x, y, usPalette[*s++]); // color 565 + } + } +} /* GIFDraw() */ + + +void * GIFOpenFile(const char *fname, int32_t *pSize) +{ + Serial.print("Playing gif: "); + Serial.println(fname); + f = FILESYSTEM.open(fname); + if (f) + { + *pSize = f.size(); + return (void *)&f; + } + return NULL; +} /* GIFOpenFile() */ + +void GIFCloseFile(void *pHandle) +{ + File *f = static_cast(pHandle); + if (f != NULL) + f->close(); +} /* GIFCloseFile() */ + +int32_t GIFReadFile(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen) +{ + int32_t iBytesRead; + iBytesRead = iLen; + File *f = static_cast(pFile->fHandle); + // Note: If you read a file all the way to the last byte, seek() stops working + if ((pFile->iSize - pFile->iPos) < iLen) + iBytesRead = pFile->iSize - pFile->iPos - 1; // <-- ugly work-around + if (iBytesRead <= 0) + return 0; + iBytesRead = (int32_t)f->read(pBuf, iBytesRead); + pFile->iPos = f->position(); + return iBytesRead; +} /* GIFReadFile() */ + +int32_t GIFSeekFile(GIFFILE *pFile, int32_t iPosition) +{ + int i = micros(); + File *f = static_cast(pFile->fHandle); + f->seek(iPosition); + pFile->iPos = (int32_t)f->position(); + i = micros() - i; +// Serial.printf("Seek time = %d us\n", i); + return pFile->iPos; +} /* GIFSeekFile() */ + +unsigned long start_tick = 0; + +void ShowGIF(char *name) +{ + start_tick = millis(); + + if (gif.open(name, GIFOpenFile, GIFCloseFile, GIFReadFile, GIFSeekFile, GIFDraw)) + { + x_offset = (dma_display->width() - gif.getCanvasWidth())/2; + if (x_offset < 0) x_offset = 0; + y_offset = (dma_display->height() - gif.getCanvasHeight())/2; + if (y_offset < 0) y_offset = 0; + Serial.printf("Successfully opened GIF; Canvas size = %d x %d\n", gif.getCanvasWidth(), gif.getCanvasHeight()); + Serial.flush(); + while (gif.playFrame(true, NULL)) + { + if ( (millis() - start_tick) > 8000) { // we'll get bored after about 8 seconds of the same looping gif + break; + } + } + gif.close(); + } + +} /* ShowGIF() */ + + + +/************************* Arduino Sketch Setup and Loop() *******************************/ +void setup() { + Serial.begin(115200); + + if(!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)){ + Serial.println("LittleFS Mount Failed"); + return; + } + + HUB75_I2S_CFG mxconfig( + PANEL_RES_X, // module width + PANEL_RES_Y, // module height + PANEL_CHAIN // Chain of panels - Horizontal width only. + ); + + // mxconfig.gpio.e = 18; + // mxconfig.clkphase = false; + // mxconfig.driver = HUB75_I2S_CFG::FM6126A; + + // Display Setup + dma_display = new MatrixPanel_I2S_DMA(mxconfig); + dma_display->begin(); + dma_display->setBrightness8(128); //0-255 + dma_display->clearScreen(); + dma_display->fillScreen(myWHITE); + + // Start going through GIFS + gif.begin(LITTLE_ENDIAN_PIXELS); + +} + +String gifDir = "/gifs"; // play all GIFs in this directory on the SD card +char filePath[256] = { 0 }; +File root, gifFile; + +void loop() +{ + while (1) // run forever + { + + root = FILESYSTEM.open(gifDir); + if (root) + { + gifFile = root.openNextFile(); + while (gifFile) + { + if (!gifFile.isDirectory()) // play it + { + + // C-strings... urghh... + memset(filePath, 0x0, sizeof(filePath)); + strcpy(filePath, gifFile.path()); + + // Show it. + ShowGIF(filePath); + + } + gifFile.close(); + gifFile = root.openNextFile(); + } + root.close(); + } // root + + delay(1000); // pause before restarting + + } // while +} + +// Other LittleFS filesystem function examples available at: +// https://randomnerdtutorials.com/esp32-write-data-littlefs-arduino/ \ No newline at end of file diff --git a/examples/PIO_TestPatterns/platformio.ini b/examples/PIO_TestPatterns/platformio.ini index 185513a..61f3492 100644 --- a/examples/PIO_TestPatterns/platformio.ini +++ b/examples/PIO_TestPatterns/platformio.ini @@ -5,7 +5,9 @@ description = HUB75 ESP32 I2S DMA test patterns example [env] platform = espressif32 -board = wemos_d1_mini32 +board = lolin_s2_mini +#platform = espressif32 +#board = denky32 lib_deps = fastled/FastLED Wire diff --git a/examples/PIO_TestPatterns/src/main.cpp b/examples/PIO_TestPatterns/src/main.cpp index 4e0edb1..803435f 100644 --- a/examples/PIO_TestPatterns/src/main.cpp +++ b/examples/PIO_TestPatterns/src/main.cpp @@ -18,43 +18,32 @@ #endif #include "main.h" -// HUB75E pinout -// R1 | G1 -// B1 | GND -// R2 | G2 -// B2 | E -// A | B -// C | D -// CLK| LAT -// OE | GND - -/* Default library pin configuration for the reference - you can redefine only ones you need later on object creation -#define R1 25 -#define G1 26 -#define BL1 27 -#define R2 14 -#define G2 12 -#define BL2 13 -#define CH_A 23 -#define CH_B 19 -#define CH_C 5 -#define CH_D 17 -#define CH_E -1 // assign to any available pin if using panels with 1/32 scan -#define CLK 16 -#define LAT 4 -#define OE 15 -*/ +#define R1_PIN 1 +#define G1_PIN 40 +#define B1_PIN 2 +#define R2_PIN 4 +#define G2_PIN 36 +#define B2_PIN 6 +#define A_PIN 8 +#define B_PIN 21 +#define C_PIN 10 +#define D_PIN 17 +#define E_PIN 34 // required for 1/32 scan panels, like 64x64px. Any available pin would do, i.e. IO32 +#define LAT_PIN 15 +#define OE_PIN 14 +#define CLK_PIN 13 + +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}; // Configure for your panel(s) as appropriate! -#define PIN_E 32 +#define E_PIN 34 #define PANEL_WIDTH 64 -#define PANEL_HEIGHT 64 // Panel height of 64 will required PIN_E to be defined. +#define PANEL_HEIGHT 64 // Panel height of 64 will required E_PIN to be defined. #ifdef VIRTUAL_PANE - #define PANELS_NUMBER 4 // Number of chained panels, if just a single panel, obviously set to 1 + #define PANELS_NUMBER 1 // Number of chained panels, if just a single panel, obviously set to 1 #else - #define PANELS_NUMBER 2 // Number of chained panels, if just a single panel, obviously set to 1 + #define PANELS_NUMBER 1 // Number of chained panels, if just a single panel, obviously set to 1 #endif #define PANE_WIDTH PANEL_WIDTH * PANELS_NUMBER @@ -62,12 +51,12 @@ #define NUM_LEDS PANE_WIDTH*PANE_HEIGHT #ifdef VIRTUAL_PANE - #define NUM_ROWS 2 // Number of rows of chained INDIVIDUAL PANELS - #define NUM_COLS 2 // Number of INDIVIDUAL PANELS per ROW + #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 + #define SERPENT false + #define TOPDOWN true #endif @@ -102,19 +91,20 @@ void setup(){ // redefine pins if required //HUB75_I2S_CFG::i2s_pins _pins={R1, G1, BL1, R2, G2, BL2, CH_A, CH_B, CH_C, CH_D, CH_E, LAT, OE, CLK}; - HUB75_I2S_CFG mxconfig(PANEL_WIDTH, PANEL_HEIGHT, PANELS_NUMBER); + HUB75_I2S_CFG mxconfig(PANEL_WIDTH, PANEL_HEIGHT, PANELS_NUMBER, _pins); - mxconfig.gpio.e = PIN_E; - mxconfig.driver = HUB75_I2S_CFG::FM6126A; // for panels using FM6126A chips + mxconfig.gpio.e = E_PIN; + mxconfig.driver = HUB75_I2S_CFG::MBI5124; // for panels using FM6126A chips + mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_8M; #ifndef VIRTUAL_PANE matrix = new MatrixPanel_I2S_DMA(mxconfig); matrix->begin(); - matrix->setBrightness8(255); + matrix->setBrightness8(128); #else chain = new MatrixPanel_I2S_DMA(mxconfig); chain->begin(); - chain->setBrightness8(255); + chain->setBrightness8(128); // create VirtualDisplay object based on our newly created dma_display object matrix = new VirtualMatrixPanel((*chain), NUM_ROWS, NUM_COLS, PANEL_WIDTH, PANEL_HEIGHT, CHAIN_TOP_LEFT_DOWN); #endif @@ -139,18 +129,18 @@ void loop(){ Serial.printf("%d ticks\n", ccount1); delay(PATTERN_DELAY); -/* + // Power supply tester // slowly fills matrix with white, stressing PSU for (int y=0; y!=PANE_HEIGHT; ++y){ for (int x=0; x!=PANE_WIDTH; ++x){ matrix->drawPixelRGB888(x, y, 255,255,255); //matrix->drawPixelRGB888(x, y-1, 255,0,0); // pls, be gentle :) - delay(10); + delay(2); } } - delay(5000); -*/ + delay(1000); + #ifndef VIRTUAL_PANE // simple solid colors @@ -387,7 +377,7 @@ void loop(){ Serial.println("\n====\n"); // take a rest for a while - delay(10000); + delay(1000); } -- cgit v1.3.1