Open bartoszx opened 2 months ago
Hi Your panel probably could be run same way as quarter scan example. See #624 issue.
I have tested 40x20 s5 panel recently.
Hi Board707 Thanks for your reply! I've been trying to follow the quarter scan example you mentioned and made some progress, although it's mostly been through a trial and error approach rather than a clear strategy. Below is my code adapted from the "four scan" example:
#include "ESP32-HUB75-MatrixPanel-I2S-DMA.h"
/* Use the Virtual Display class to re-map co-ordinates such that they draw
* correctly on a 32x16 1/8 Scan panel (or chain of such panels).
*/
#include "ESP32-VirtualMatrixPanel-I2S-DMA.h"
#define R1_PIN 25
#define G1_PIN 26
#define B1_PIN 27
#define R2_PIN 14
#define G2_PIN 12
#define B2_PIN 13
#define A_PIN 23
#define B_PIN 22
#define C_PIN 5
#define D_PIN 17
#define E_PIN -1
#define CLK_PIN 16
#define LAT_PIN 4
#define OE_PIN 15
// Panel configuration
#define PANEL_RES_X 80 // Number of pixels wide of each INDIVIDUAL panel module.
#define PANEL_RES_Y 40 // 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 false
#define TOPDOWN false
// placeholder for the matrix object
MatrixPanel_I2S_DMA *dma_display = nullptr;
// placeholder for the virtual display object
VirtualMatrixPanel *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::ICN2038S; // 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 FourScanPanellay object based on our newly created dma_display object
FourScanPanel = new VirtualMatrixPanel((*dma_display), NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y);
// THE IMPORTANT BIT BELOW!
FourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_32PX_HIGH);
}
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++)
{
FourScanPanel->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++)
{
FourScanPanel->drawLine(i+offset, 0, i+offset, 7, dma_display->color565(0, 0, 255)); // blue
FourScanPanel->drawLine(i+offset, 8, i+offset, 15, dma_display->color565(0, 128,0)); // g
FourScanPanel->drawLine(i+offset, 16, i+offset, 23, dma_display->color565(128, 0,0)); // red
FourScanPanel->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++)
{
FourScanPanel->setTextColor(FourScanPanel->color565(255, 255, 255));
FourScanPanel->setCursor(i*PANEL_RES_X+7, FourScanPanel->height()/3);
// Red text inside red rect (2 pix in from edge)
FourScanPanel->print("Panel " + String(i+1));
FourScanPanel->drawRect(1,1, FourScanPanel->width()-2, FourScanPanel->height()-2, FourScanPanel->color565(255,0,0));
// White line from top left to bottom right
FourScanPanel->drawLine(0,0, FourScanPanel->width()-1, FourScanPanel->height()-1, FourScanPanel->color565(255,255,255));
}
delay(2000);
dma_display->clearScreen();
} // end loop
also tryin to change
if (panel_scan_rate == FOUR_SCAN_32PX_HIGH)
in ESP32-HUB75-MatrixPanel-I2S-DMA.h
here is how it look now
Hi In order to run your panel you need to change ESP32-HUB75-MatrixPanel-I2S-DMA.h file. Currently your code using a settings for 32 height panel.
But the better way rather than changing the library is create a custom virtual class as shown in example in #622 discussion. Sorry I can't dive in detail, I am in airport now :)
Try different changes and some of the tests seem to look okay (video), but it can't display the simple text "hello". Board707, thanks for your help, but I am afraid it is too complicated for my level of knowledge. We are building a smart basketball court and we need a simple display to show e.g. the score and player information. I thought that it would be an easy job, but it is not. I think I will look for something that works almost out of the box because we still need to integrate it with MQTT, etc., so I'm afraid this part could take a lot of time to learn.
Here is my latest code and results
/*************************************************************************
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"
#include <Adafruit_GFX.h> // Include the library for the display functions
#define R1_PIN 25
#define G1_PIN 26
#define B1_PIN 27
#define R2_PIN 14
#define G2_PIN 12
#define B2_PIN 13
#define A_PIN 23
#define B_PIN 22
#define C_PIN 5
#define D_PIN 17
#define E_PIN -1
#define CLK_PIN 16
#define LAT_PIN 4
#define OE_PIN 15
/* ================================================== */
// 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 80 // Number of pixels wide of each INDIVIDUAL panel module.
#define PANEL_RES_Y 40 // 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
// 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::ICN2038S;
//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);
dma_display->begin(); // Start the display
dma_display->setTextColor(dma_display->color565(255, 255, 255)); // Set text color to white
dma_display->setTextSize(1); // Set text size. Adjust as needed for your specific panel size
dma_display->setCursor(0, 0); // Set cursor to top left corner of the display
dma_display->println("hello"); // Print "hello" at the current cursor position
}
// 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(255, 0, 0));
delay(30);
}
}
delay(2000);
dma_display->clearScreen();
} // end loop
results
p.s check out our family project https://www.instagram.com/gamechangair?igsh=MW5jbm9nemJ4MHl3dA==
This is just a pixel mapping issue because of the weird way this panel works.
The brute force way of fixing this is simply having a function that converts coordinates from what you want to what the panel needs.
One way to figure this out is make a simple sketch that starts at pixel x 0, y 0 and then slowly increments (lights up LEDs) line by line to x 79, y 39.
Make notes for the first couple of lines and work on a lookup function using a simple array to convert x to panelx and y to panely.
There's a pattern here in that it looks like the panel is broken into x*x sub squares of pixels.
Painful, but this library supports this panel electrically, but if the manufacturer chains physical pixels in weird ways you'll need to figure out the mapping.
Alternatively, just go buy a standard 64x64 pixel panel and it'll probably work out of the box.
@mrcodetastic thanks for clarification will it work out of the box https://a.aliexpress.com/_Evn9Fzt ?
I do not give any guarantees on any sellers, in particular Chinese ones as they'll send anything, regardless of the description.
Doesn't look like the right one to me, you'd want a 1/32 scan for a 64*64 panel.
Hi, @bartoszx
You took my example for the 32x16 s4 matrix and used it as is. But it won't work that way. Each type of matrix requires its own code. The example needs to be edited for your 80x40 panel.
Please change the getCoords()
method in your class this way and show the resulting video.
// 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 = 80; // pixel base
if ((coords.y % 20 ) < 10)
{
coords.x += (coords.x / pxbase + 1) * pxbase;
}
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 /20 ) * 10 + (coords.y % 10);
return coords;
}
(take note that it is not a final solution, but just a test)
@board707 Here is how it looks now
Looks good. Now change the pixbase to 16 and test again.
It seems to me that the problem has solved. You can now use this virtual class in your project. The pixel mapping also should works for multiple panels.
It works 🍾 Thank you @board707
Looks good but I suggest you shorten the length of the wires from the esp32 to no greater than 10cm to avoid interference.
There seems to be some random artefacts showing... Likely caused by electrical interference.
@mrcodetastic thanks for tip, didnt know that
Hi @mrcodetastic one more question :) Everything works fine with text, but when trying to show a gif, after 2 days of trying different approaches, code, and parameters, nothing seems to work. I tried using different gifs, and each one looks jumbled.
my code
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
#include <ESP32-VirtualMatrixPanel-I2S-DMA.h>
#include "virtualdispaly.h"
#include <LittleFS.h>
#include <AnimatedGIF.h>
#define FILESYSTEM LittleFS
// Define panel configuration for 4 panels (2x2)
//#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 4 // Total number of panels chained one to another
#define PANEL_RES_X 80 // Number of pixels wide of each INDIVIDUAL panel module.
#define PANEL_RES_Y 40 // Number of pixels tall of each INDIVIDUAL panel module.
// MatrixPanel_I2S_DMA *dma_display = nullptr;
VirtualMatrixPanel *virtualDisp = nullptr;
uint16_t myBLACK, myWHITE, myRED, myGREEN, myBLUE;
AnimatedGIF gif;
File f;
int x_offset, y_offset;
void GIFDraw(GIFDRAW *pDraw)
{
uint8_t *s;
uint16_t *d, *usPalette, usTemp[320];
int x, y, iWidth;
usPalette = pDraw->pPalette;
y = pDraw->iY + pDraw->y; // current line
s = pDraw->pPixels;
if (pDraw->ucDisposalMethod == 2) // restore to background color
{
for (x = 0; x < iWidth; x++)
{
if (s[x] == pDraw->ucTransparent)
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]);
}
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
{
s = pDraw->pPixels;
// Translate the 8-bit pixels through the RGB565 palette (already byte reversed)
for (x = 0; x < pDraw->iWidth; x++)
{
dma_display->drawPixel(x, y, usPalette[*s++]);
}
}
} /* 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;
}
void GIFCloseFile(void *pHandle)
{
File *f = static_cast<File *>(pHandle);
if (f != NULL)
f->close();
}
int32_t GIFReadFile(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen)
{
int32_t iBytesRead;
iBytesRead = iLen;
File *f = static_cast<File *>(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); // Poprawka: int32_t zamiast int3232
pFile->iPos = f->position();
return iBytesRead;
}
int32_t GIFSeekFile(GIFFILE *pFile, int32_t iPosition)
{
int i = micros();
File *f = static_cast<File *>(pFile->fHandle);
f->seek(iPosition);
pFile->iPos = (int32_t)f->position();
i = micros() - i;
return pFile->iPos;
}
unsigned long start_tick = 0;
void ShowGIF(char *name)
{
start_tick = millis();
if (gif.open(name, GIFOpenFile, GIFCloseFile, GIFReadFile, GIFSeekFile, GIFDraw))
{
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();
}
}
void setup()
{
Serial.begin(115200);
delay(1000);
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;
dma_display = new MatrixPanel_I2S_DMA(mxconfig);
dma_display->begin();
dma_display->setBrightness8(40); // Ustaw jasność na 64 (możesz dostosować tę wartość)
dma_display->clearScreen();
// virtualDisp = new VirtualMatrixPanel((*dma_display), 2, 2, PANEL_RES_X, PANEL_RES_Y, CHAIN_TOP_RIGHT_DOWN);
virtualDisp = new VirtualMatrixPanel((*dma_display), NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, VIRTUAL_MATRIX_CHAIN_TYPE);
// new EightPxBasePanel((*dma_display), NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, VIRTUAL_MATRIX_CHAIN_TYPE);
myBLACK = virtualDisp->color565(0, 0, 0);
myWHITE = virtualDisp->color565(255, 255, 255);
myRED = virtualDisp->color565(255, 0, 0);
myGREEN = virtualDisp->color565(0, 255, 0);
myBLUE = virtualDisp->color565(0, 0, 255);
Serial.println("Starting AnimatedGIFs Sketch");
// Start filesystem
Serial.println(" * Loading LittleFS");
if (!LittleFS.begin()) {
Serial.println("LittleFS Mount Failed");
}
virtualDisp->fillScreen(myBLACK);
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();
Serial.println("openNextFile: ");
while (gifFile)
{
if (!gifFile.isDirectory()) // play it
{
// C-strings... urghh...
memset(filePath, 0x0, sizeof(filePath));
strcpy(filePath, gifFile.path());
// Show it.
Serial.println("Show: ");
Serial.println(filePath);
ShowGIF(filePath);
}
gifFile.close();
gifFile = root.openNextFile();
}
root.close();
} // root
delay(400); // pause before restarting
} // while
}
virtualdisplay.h
#define R1_PIN 25
#define G1_PIN 26
#define B1_PIN 27
#define R2_PIN 14
#define G2_PIN 12
#define B2_PIN 13
#define A_PIN 23
#define B_PIN 19
#define C_PIN 5
#define D_PIN 17
#define E_PIN -1
#define CLK_PIN 16
#define LAT_PIN 4
#define OE_PIN 15
/* ================================================== */
// 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
// 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 = 16; // pixel base
if ((coords.y % 20 ) < 10)
{
coords.x += (coords.x / pxbase + 1) * pxbase;
}
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 /20 ) * 10 + (coords.y % 10);
return coords;
}
/* ================================================== */
// Panel configuration
#define PANEL_RES_X 80 // Number of pixels wide of each INDIVIDUAL panel module.
#define PANEL_RES_Y 40 // 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
// placeholder for the matrix object
MatrixPanel_I2S_DMA *dma_display = nullptr;
// placeholder for the virtual display object
EightPxBasePanel *FourScanPanel = nullptr;
@board707 is that problem with my 1/10 scan?
In the void GIFDraw(GIFDRAW *pDraw)
function try to use a
virtualDisp->drawPixel(...)
anywhere instead of dma_display->
I've tried that as well. Here's another example I've checked.
void GIFDraw(GIFDRAW *pDraw)
{
uint8_t *s;
uint16_t *d, *usPalette, usTemp[320];
int x, y, iWidth;
iWidth = pDraw->iWidth;
if (iWidth > 80) // Adjust for total width of 4 panels horizontally
iWidth = 80;
usPalette = pDraw->pPalette;
y = pDraw->iY + pDraw->y + y_offset -0; // Add y_offset
s = pDraw->pPixels;
if (pDraw->ucDisposalMethod == 2) // restore to background color
{
for (x = 0; x < iWidth; x++)
{
if (s[x] == pDraw->ucTransparent)
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++)
{
virtualDisp->drawPixel(x + xOffset, y, usTemp[xOffset]); // Add x_offset
}
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; x < pDraw->iWidth; x++)
{
virtualDisp->drawPixel(x, y, usPalette[*s++]); // Add x_offset
}
}
}
everytime, same results In another test I ve tried to display static harts
const unsigned short RedHeart[2048] PROGMEM={
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0010 (16) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0020 (32) pixels
...
FourScanPanel = new EightPxBasePanel((*dma_display), NUM_ROWS, NUM_COLS,PANEL_RES_X, PANEL_RES_Y, VIRTUAL_MATRIX_CHAIN_TYPE);
...
FourScanPanel->drawRGBBitmap(0, 0, (const uint16_t *)RedHeart, 80, 40);
but no luck
It looks like the real and virtual coordinates are mixed up somewhere else - just like I indicated in the function GIFDraw()
.
All calls to the real dma_display
object need to be replaced with a virtual one, except for the very first one where the dma_display is used to initialize the virtual.
You are right when you pointed out the dependency, it dawned on me, and I changed the code Works perfect. Thanks again
Hi,
I've got this panel (https://a.aliexpress.com/_EJLWwpl) and I've been trying to run some demos, but no luck so far. All I'm getting are some random pixels and deformed output. I'm just trying to display some simple text. Can anyone tell me how to do it, or if it's even possible?
Attached are some images of my panel and the ESP32.
Here's an example of the code I've been trying to run (you'll find an enclosed image showing how it looks).
output![Zrzut ekranu 2024-05-1 o 20 52 54](https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA/assets/195576/47314a7c-e3b2-4ce9-843f-3b92f2c28571)