mrcodetastic / ESP32-HUB75-MatrixPanel-DMA

An Adafruit GFX Compatible Library for the ESP32, ESP32-S2, ESP32-S3 to drive HUB75 LED matrix panels using DMA for high refresh rates. Supports panel chaining.
MIT License
974 stars 215 forks source link

Need help with single 64x32 matrix with 1/8 scan rate #683

Open brianmackessy opened 1 month ago

brianmackessy commented 1 month ago

I'm having a lot of issues with 64x32 matrix with a 1/8 scan rate, in this code I am trying to draw a simple white border around the matrix. I am driving the matrix with a Adafruit Matrix Portal S3.

I tried to follow the instructions here to no avail: https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA/issues/578#issue-2106096259

All messed up!

Photo on 10-3-24 at 6 22 PM

ESP32-VirtualMatrixPanel-I2S-DMA.h (Updated)

    if (panel_scan_rate == FOUR_SCAN_32PX_HIGH)
    {
        if ((coords.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
        }

        // Real number of DMA y rows is half reality
        // coords.y = (y / 16)*8 + (y & 0b00000111);
        //
        coords.y = (coords.y >> 4) * 8 + (coords.y & 0b00000111);
    }

sktech.ino

#include <Arduino.h>
#include <Adafruit_GFX.h>
#include "ESP32-VirtualMatrixPanel-I2S-DMA.h"
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>

// Pins
#define R1_PIN 42
#define G1_PIN 41
#define B1_PIN 40
#define R2_PIN 38
#define G2_PIN 39
#define B2_PIN 37

#define A_PIN  45
#define B_PIN  36
#define C_PIN  48
#define D_PIN  35
#define E_PIN  21

#define LAT_PIN 47
#define OE_PIN  14
#define CLK_PIN  2

// Define panel size
#define PANEL_RES_X 64
#define PANEL_RES_Y 32

// Define the number of rows and columns in the panel matrix
#define NUM_ROWS 1
#define NUM_COLS 1
#define PANEL_CHAIN (NUM_ROWS * NUM_COLS)

// Define the virtual matrix chain type
#define VIRTUAL_MATRIX_CHAIN_TYPE CHAIN_TOP_RIGHT_DOWN

#define SERPENT true
#define TOPDOWN true

// Declare pointers for DMA display and virtual display
MatrixPanel_I2S_DMA *dma_display = nullptr;
VirtualMatrixPanel *virtualDisp = nullptr;

// Function to draw text with background
void drawTextWithBackground(int x, int y, const char *text, uint16_t textColor) {
  virtualDisp->setTextColor(textColor);
  virtualDisp->setTextSize(3);
  virtualDisp->setCursor(x, y);
  virtualDisp->print(text);
}

void setup() {
  Serial.begin(115200);

       // 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
  };

  // Initialize HUB75 configuration with panel dimensions and chain settings
  HUB75_I2S_CFG mxconfig(
    PANEL_RES_X * 2,
    PANEL_RES_Y / 2,
    PANEL_CHAIN
    ,_pins
  );

  mxconfig.clkphase = false;
  //mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_20M;
  mxconfig.driver   = HUB75_I2S_CFG::ICN2038S;    // Not sure what this is???

  // Initialize DMA display
  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%

  // Check if DMA display began successfully
  if (!dma_display->begin()) {
    Serial.println("****** !KABOOM! I2S memory allocation failed ***********");
    return;
  }
  dma_display->setBrightness8(192);

  dma_display->clearScreen();
  delay(500);

  // Initialize virtual display with the DMA display and panel settings
  virtualDisp = new VirtualMatrixPanel((*dma_display), NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, VIRTUAL_MATRIX_CHAIN_TYPE);
  virtualDisp->setPhysicalPanelScanRate(FOUR_SCAN_32PX_HIGH);
}

void loop() {
  // Define the height of each section of the display
  int sectionHeight = virtualDisp->height() / 4;

  // Draw a rectangle around the display edges
  virtualDisp->drawRect(1, 1, virtualDisp->width() - 2, virtualDisp->height() - 2, virtualDisp->color565(255, 0, 0));

  // Draw a diagonal line across the display
  virtualDisp->drawLine(0, 0, virtualDisp->width() - 1, virtualDisp->height() - 1, virtualDisp->color565(255, 255, 255));
  delay(2000);

  // Draw text in the middle of the display
  // drawTextWithBackground(0, virtualDisp->height() / 2 - 8, "    hi", virtualDisp->color565(0, 0, 255));
  // delay(10000);
}
board707 commented 1 month ago

Hi try to use a new approach to the issue - see the code in #618

brianmackessy commented 1 month ago

@board707 I upgraded to the more modern solution you suggested, but I am running into an issue where only half of the screen is being filled with red. I might try to buy 2 outdoor 32x32 matrices. People seem to be having more luck with them. Anything you can tell me helps.

https://github.com/user-attachments/assets/417a2441-9628-41e2-bbe0-3e36e77d99a7

/*************************************************************************
   Description:

   The underlying implementation of the ESP32-HUB75-MatrixPanel-I2S-DMA only
   supports output to HALF scan panels - which means outputting
   two lines at the same time, 16 or 32 rows apart if a 32px or 64px high panel
   respectively.
   This cannot be changed at the DMA layer as it would require a messy and complex
   rebuild of the library's internals.

   However, it is possible to connect QUARTER (i.e. FOUR lines updated in parallel)
   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.

 **************************************************************************/

/* Use a custom Virtual Display class to re-map co-ordinates such that they draw
   correctly on a Four Scan panel (or chain of such panels).
*/
#include "ESP32-VirtualMatrixPanel-I2S-DMA.h"

/* ================================================== */
// Define custom class derived from VirtualMatrixPanel
class EightPxBasePanel : public VirtualMatrixPanel
{
  public:
    using VirtualMatrixPanel::VirtualMatrixPanel; // inherit VirtualMatrixPanel's constructor(s)

  protected:

    VirtualCoords getCoords(int16_t x, int16_t y);  // custom getCoords() method for specific pixel mapping

};

// define custom getCoords() method for specific pixel mapping
inline VirtualCoords EightPxBasePanel ::getCoords(int16_t x, int16_t y) {

  coords = VirtualMatrixPanel::getCoords(x, y); // first call base class method 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;
  }

 if ((coords.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
        }

        coords.y = (coords.y >> 4) * 8 + (coords.y & 0b00000111);

  return coords;
}
/* ================================================== */
// 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

// ^^^ 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
#define VIRTUAL_MATRIX_CHAIN_TYPE CHAIN_TOP_RIGHT_DOWN

// Pins
#define R1_PIN 42
#define G1_PIN 41
#define B1_PIN 40
#define R2_PIN 38
#define G2_PIN 39
#define B2_PIN 37

#define A_PIN  45
#define B_PIN  36
#define C_PIN  48
#define D_PIN  35
#define E_PIN  21

#define LAT_PIN 47
#define OE_PIN  14
#define CLK_PIN  2

// placeholder for the matrix object
MatrixPanel_I2S_DMA *dma_display = nullptr;

// placeholder for the virtual display object
EightPxBasePanel   *FourScanPanel = 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("*****************************************************");

  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(40);    // 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 FourScanPanellay object based on our newly created dma_display object
  FourScanPanel = new EightPxBasePanel ((*dma_display), NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y,    VIRTUAL_MATRIX_CHAIN_TYPE);

  // THE IMPORTANT BIT BELOW!
  // FOUR_SCAN_16PX_HIGH
  // FOUR_SCAN_32PX_HIGH
// **** You don't need to set PhysicalPanelScanRate when use your own virtual panel class
 // FourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_16PX_HIGH);
}

// Test the pixel mapping - fill the panel pixel by pixel
void loop() {

  for (int i = 0; i < FourScanPanel->height(); i++)
  {
    for (int j = 0; j < FourScanPanel->width(); j++)
    {

      FourScanPanel->drawPixel(j, i, FourScanPanel->color565(40, 0, 0));
      delay(20);
    }
  }

} // end loop
brianmackessy commented 1 month ago

Also is there any recommendations for hardware? I need an outdoor 64x32. I'm open to buying a new microcontroller and led matrix to get this project moving. I'm currently using an Adafruit Matrix Portal S3 and 64x32 Outdoor Panel .

board707 commented 1 month ago

Hi According to the video, the code is definitely not compatible with your panel. Could you look up all the chips on the panel and list it's ID codes on the topic?