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

P10 32x16 1/4 SM16208SJ, DP74HC138B, MW245B, MW4953F - need help with tuning #677

Open YuriiKrop opened 2 months ago

YuriiKrop commented 2 months ago

Hi there! I have 2 such panel and it fill as shown in the video:

https://github.com/user-attachments/assets/04af0f69-7d3c-47d3-aea5-318c1011493c

Help me with mapping, please

This is code fragment of getCoords() function I use:

  if ((coords.y & 4) == 0)
  {
    coords.x = (coords.x / pxbase) * 2 * pxbase  + pxbase  + 7 - (coords.x & 0x7);
  }
  else
  {
    coords.x += (coords.x / pxbase) * pxbase; 
  }
  coords.y = (coords.y >> 3) * 4 + (coords.y & 0b00000011);

  return coords;
davemaster commented 2 months ago

Greetings,

First, try to slowdown a little, but this was made already in hzeller rpi-rgb-led-matrix github, you only have to adapt it to this...

Second, provide your full code... better way, all files. Thanks

board707 commented 2 months ago

Hi @YuriiKrop As I see, you got the coords conversion code from the discussion #622, isn't it? But the pattern of your panel is different from one, used in the discussion and the code is not suited.

I could help you to adjust the code. Firstly, we need step back and to see the pattern without any conversion , as was done in Part 2 of the #622 tutorial. Please, show your full sketch, used to generate the video.

YuriiKrop commented 2 months ago

Thanks for the quick reply! Here is the full code for the previous video. Yes, it from #622 discussion (your last message), with uint8_t pxbase = 8; and it do 100% fill my matrix. Code without pxbase don't fill matrix on 100%, so I decide take this code.

/*************************************************************************
   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 32x16 1/4 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;
  }

  uint8_t pxbase = 8;  // pixel base
  if ((coords.y & 4) == 0)
  {
    coords.x = (coords.x / pxbase) * 2 * pxbase  + pxbase  + 7 - (coords.x & 0x7); // 1st, 3rd 'block' of 8 rows of pixels, offset by panel width in DMA buffer
  }
  else
  {
    coords.x += (coords.x / pxbase) * pxbase; // 2nd, 4th 'block' of 8 rows of pixels, offset by panel width in DMA buffer
  }

  coords.y = (coords.y >> 3) * 4 + (coords.y & 0b00000011);

  return coords;
}
/* ================================================== */
// Panel configuration
#define PANEL_RES_X 32 // Number of pixels wide of each INDIVIDUAL panel module. 
#define PANEL_RES_Y 16 // 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
#define VIRTUAL_MATRIX_CHAIN_TYPE CHAIN_TOP_RIGHT_DOWN

// 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("*****************************************************");

  /*
       // 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(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);
    }
  }

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

} // end loop
YuriiKrop commented 2 months ago

Here is pinout my ESP32:

#define R1_PIN_DEFAULT  4
#define G1_PIN_DEFAULT  12
#define B1_PIN_DEFAULT  13
#define R2_PIN_DEFAULT  14
#define G2_PIN_DEFAULT  15
#define B2_PIN_DEFAULT  21

#define A_PIN_DEFAULT   16
#define B_PIN_DEFAULT   17
#define C_PIN_DEFAULT   -1
#define D_PIN_DEFAULT   -1
#define E_PIN_DEFAULT   -1 // IMPORTANT: Change to a valid pin if using a 64x64px panel.

#define LAT_PIN_DEFAULT 32
#define OE_PIN_DEFAULT  33
#define CLK_PIN_DEFAULT 27

And I should say I use quite long flat cable, about 1 meter. If necessary, I will make it shorter.

YuriiKrop commented 2 months ago

And if I use variant without pxbase

if (((coords.y / 5)%2) == 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 / 10) * 5 + (coords.y % 5);
  return coords;

I get such picture

https://github.com/user-attachments/assets/91bf9e65-9f89-45f5-8057-b69357d6a960

board707 commented 2 months ago

Hi First - please use a single panel for testing and change the settings accordingly:

#define NUM_ROWS 1 // Number of rows of chained INDIVIDUAL PANELS
#define NUM_COLS 1 // Number of INDIVIDUAL PANELS per ROW

Next, change the conversion as shown below:

if ((virt_y & 4) == 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 = (virt_y >> 3) * 4 + (virt_y & 0b00000011);

Run it and show the video.

YuriiKrop commented 2 months ago

If I change it in code, I get Compilation error: 'virt_y' was not declared in this scope

So, I uncommented FourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_16PX_HIGH);

set

#define NUM_ROWS 1 
#define NUM_COLS 1 

and replace fragment in ESP32-VirtualMatrixPanel-I2S-DMA.h

    {
       /* if ((coords.y & 4) == 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 >> 3) * 4 + (coords.y & 0b00000011);*/
    //***********************
    if ((virt_y & 4) == 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 = (virt_y >> 3) * 4 + (virt_y & 0b00000011);

    //*************************
    }

    return coords;

https://github.com/user-attachments/assets/8e7a01a0-c271-40c2-9627-04df5eba6800

board707 commented 2 months ago

I get Compilation error: 'virt_y' was not declared in this scope

Sorry, return to your code and try this

if ((coords.y & 4) == 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 >> 3) * 4 + (coords.y & 0b00000011);

As I see, you steel to use a two panels. Please use the only one.

YuriiKrop commented 2 months ago

I replaced the video because I forgot to comment FourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_16PX_HIGH);

https://github.com/user-attachments/assets/a9261d8c-6120-4886-a75a-56b6de8d9614

Ok, I have disabled the second panel physically

board707 commented 2 months ago

Hmmm... Your video doesn't look quite right - it looks like two lines are being filled at the same time. It shouldn't be like that. Something wrong with A B C D E lines...

As for the pattern, this panel has a 16 pixel base.

Try this code

uint8_t pxbase =16;   // pixel base
if ((coords.y & 4) == 0)
        {
            coords.x += ((coords.x / pxbase) + 1) * pxbase; // 1st, 3rd 'block' of 8 rows of pixels, offset by panel width in DMA buffer
        }
        else
        {
            coords.x += (coords.x / pxbase) * pxbase; // 2nd, 4th 'block' of 8 rows of pixels, offset by panel width in DMA buffer
        }

 coords.y = (coords.y >> 3) * 4 + (coords.y & 0b00000011);

By the way, increase the delay in the loop() between outputting individual pixels to make it easier to see how the lines are being filled.

YuriiKrop commented 2 months ago

https://github.com/user-attachments/assets/abd43fbb-0694-448a-a83c-e79bf8f4ad4a

YuriiKrop commented 2 months ago

A, B, C address lines are connected But I think line C doesn't matter in 1/4 In any case, disconnecting it has no effect

board707 commented 2 months ago

Thanks to the longer delay, I now see the lines not being filled at the same time, but one pixel from the top, one from the bottom - and so on. Interesting structure, I have not seen such. For testing further, try the last code, but set pixel base 1

YuriiKrop commented 2 months ago

Wow!

https://github.com/user-attachments/assets/35d41f49-8dc5-4696-bf3e-b1bbb1a94c81

YuriiKrop commented 2 months ago

I connected the second panel and it works great.

I still don't believe it. I think I tried all the libraries, and several different arduino modules. Also before this I had other type panels, but they didn't show any signs of life at all, so I changed them to these. Thank you very much! You are a wizard, @board707

board707 commented 2 months ago

You are welcome :)

If using ESP32 is not a requirement, you can look at my DMD_STM32 library for RGB panels. It works on STM32 and rp2040 controllers. The advantage is that its code supports panels of practically any size, row driver type and pixel pattern, excluding only S-PWM type drivers.

YuriiKrop commented 2 months ago

Actually I saw your library a couple of weeks ago and appreciated it as powerfull. Especially noted the presence of the cyrillic fonts, may be it will be usefull for me. And then I saw STM32 instead ESP32 and a little upset. My project is amateur and based on ESP32, but now there is a reason to look at more advanced STM32 in a future. In the meantime, I'll be studying this library. Thanks again!

board707 commented 2 months ago

@YuriiKrop Keep in mind that the library also supports Raspberry Pico RP2040 boards. Perhaps this will be more convenient for you than stm32