aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormrfaptastic <12006953+mrfaptastic@users.noreply.github.com>2023-03-12 09:13:55 +0000
committermrfaptastic <12006953+mrfaptastic@users.noreply.github.com>2023-03-12 09:13:55 +0000
commitedfadc76503883d4ae5bfea90d5b0f1def6e5486 (patch)
tree4b6785d22a250fc7887e609aec068707dfe4dd95
parent1bb96e01754b7bdbbeffc6c4367760176b08e4cd (diff)
Rewrite of VirtualMatrixPanel
Fix chaining issue that was mentioned in #402 Compatibility breaking change to now explicitly define chaining type: CHAIN_TOP_LEFT_DOWN, CHAIN_TOP_RIGHT_DOWN, CHAIN_BOTTOM_LEFT_UP, CHAIN_BOTTOM_RIGHT_UP
-rw-r--r--doc/Panel_Chaining_Types.odsbin0 -> 24331 bytes
-rw-r--r--doc/VirtualMatrixPanel (old).odp (renamed from examples/ChainedPanels/VirtualMatrixPanel.odp)bin127435 -> 127435 bytes
-rw-r--r--doc/VirtualMatrixPanel.odpbin0 -> 240953 bytes
-rw-r--r--doc/VirtualMatrixPanel.pdfbin0 -> 1023141 bytes
-rw-r--r--examples/ChainedPanels/ChainedPanels.ino214
-rw-r--r--examples/ChainedPanels/README.md9
-rw-r--r--examples/ChainedPanels/VirtualMatrixPanel.pdfbin401320 -> 0 bytes
-rw-r--r--src/ESP32-VirtualMatrixPanel-I2S-DMA.h262
-rw-r--r--src/ESP32-VirtualMatrixPanel-I2S-DMA.h-old363
9 files changed, 608 insertions, 240 deletions
diff --git a/doc/Panel_Chaining_Types.ods b/doc/Panel_Chaining_Types.ods
new file mode 100644
index 0000000..74748c5
--- /dev/null
+++ b/doc/Panel_Chaining_Types.ods
Binary files differ
diff --git a/examples/ChainedPanels/VirtualMatrixPanel.odp b/doc/VirtualMatrixPanel (old).odp
index 4e3a066..4e3a066 100644
--- a/examples/ChainedPanels/VirtualMatrixPanel.odp
+++ b/doc/VirtualMatrixPanel (old).odp
Binary files differ
diff --git a/doc/VirtualMatrixPanel.odp b/doc/VirtualMatrixPanel.odp
new file mode 100644
index 0000000..748a211
--- /dev/null
+++ b/doc/VirtualMatrixPanel.odp
Binary files differ
diff --git a/doc/VirtualMatrixPanel.pdf b/doc/VirtualMatrixPanel.pdf
new file mode 100644
index 0000000..f6e7157
--- /dev/null
+++ b/doc/VirtualMatrixPanel.pdf
Binary files differ
diff --git a/examples/ChainedPanels/ChainedPanels.ino b/examples/ChainedPanels/ChainedPanels.ino
index 33379c6..1343d9b 100644
--- a/examples/ChainedPanels/ChainedPanels.ino
+++ b/examples/ChainedPanels/ChainedPanels.ino
@@ -1,158 +1,67 @@
/******************************************************************************
- -----------
- Steps to use
- -----------
+ -------------------------------------------------------------------------
+ Steps to create a virtual display made up of a chain of panels in a grid
+ -------------------------------------------------------------------------
- 1) In the sketch (i.e. this example):
+ Read the documentation!
+ https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-DMA/tree/master/examples/ChainedPanels
+
+ tl/dr:
- - Set values for NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, PANEL_CHAIN.
- There are comments beside them explaining what they are in more detail.
+ - Set values for NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, PANEL_CHAIN_TYPE.
+
- Other than where the matrix is defined and matrix.begin in the setup, you
should now be using the virtual display for everything (drawing pixels, writing text etc).
You can do a find and replace of all calls if it's an existing sketch
(just make sure you don't replace the definition and the matrix.begin)
+
- If the sketch makes use of MATRIX_HEIGHT or MATRIX_WIDTH, these will need to be
replaced with the width and height of your virtual screen.
Either make new defines and use that, or you can use virtualDisp.width() or .height()
- Thanks to:
-
- * Brian Lough for the original example as raised in this issue:
- https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/26
-
- YouTube: https://www.youtube.com/brianlough
- Tindie: https://www.tindie.com/stores/brianlough/
- Twitter: https://twitter.com/witnessmenow
-
- * Galaxy-Man for the kind donation of panels make/test that this is possible:
- https://github.com/Galaxy-Man
-
*****************************************************************************/
+// 1) Include key virtual display library
+ #include <ESP32-VirtualMatrixPanel-I2S-DMA.h>
-
- /******************************************************************************
- * VIRTUAL DISPLAY / MATRIX PANEL CHAINING CONFIGURATION
- *
- * Note 1: If chaining from the top right to the left, and then S curving down
- * then serpentine_chain = true and top_down_chain = true
- * (these being the last two parameters of the virtualDisp(...) constructor.
- *
- * Note 2: If chaining starts from the bottom up, then top_down_chain = false.
- *
- * Note 3: By default, this library has serpentine_chain = true, that is, every
- * second row has the panels 'upside down' (rotated 180), so the output
- * pin of the row above is right above the input connector of the next
- * row.
-
- Example 1 panel chaining:
- +-----------------+-----------------+-------------------+
- | 64x32px PANEL 3 | 64x32px PANEL 2 | 64x32px PANEL 1 |
- | ------------ <-------- | ------------xx |
- | [OUT] | [IN] | [OUT] [IN] | [OUT] [ESP IN] |
- +--------|--------+-----------------+-------------------+
- | 64x32px|PANEL 4 | 64x32px PANEL 5 | 64x32px PANEL 6 |
- | \|/ ----------> | -----> |
- | [IN] [OUT] | [IN] [OUT] | [IN] [OUT] |
- +-----------------+-----------------+-------------------+
-
- Example 1 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 2 // Number of rows of chained INDIVIDUAL PANELS
- #define NUM_COLS 3 // Number of INDIVIDUAL PANELS per ROW
-
- virtualDisp(dma_display, NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, true, true);
-
- = 192x64 px virtual display, with the top left of panel 3 being pixel co-ord (0,0)
-
- ==========================================================
-
- Example 2 panel chaining:
-
- +-------------------+
- | 64x32px PANEL 1 |
- | ----------------- |
- | [OUT] [ESP IN] |
- +-------------------+
- | 64x32px PANEL 2 |
- | |
- | [IN] [OUT] |
- +-------------------+
- | 64x32px PANEL 3 |
- | |
- | [OUT] [IN] |
- +-------------------+
- | 64x32px PANEL 4 |
- | |
- | [IN] [OUT] |
- +-------------------+
-
- Example 2 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 4 // Number of rows of chained INDIVIDUAL PANELS
- #define NUM_COLS 1 // Number of INDIVIDUAL PANELS per ROW
-
- virtualDisp(dma_display, NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, true, true);
-
- virtualDisp(dma_display, NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, true, true);
-
- = 128x64 px virtual display, with the top left of panel 1 being pixel co-ord (0,0)
-
- ==========================================================
-
- Example 3 panel chaining (bottom up):
-
- +-----------------+-----------------+
- | 64x32px PANEL 4 | 64x32px PANEL 3 |
- | <---------- |
- | [OUT] [IN] | [OUT] [in] |
- +-----------------+-----------------+
- | 64x32px PANEL 1 | 64x32px PANEL 2 |
- | ----------> |
- | [ESP IN] [OUT] | [IN] [OUT] |
- +-----------------+-----------------+
-
- Example 1 configuration:
-
+// 2) Set 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 2 // Number of rows of chained INDIVIDUAL PANELS
#define NUM_COLS 2 // Number of INDIVIDUAL PANELS per ROW
+ #define PANEL_CHAIN NUM_ROWS*NUM_COLS // total number of panels chained one to another
- virtualDisp(dma_display, NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, true, false);
-
- = 128x64 px virtual display, with the top left of panel 4 being pixel co-ord (0,0)
-
-*/
-
+ /* Configure the serpetine chaining approach. Options are:
+ CHAIN_TOP_LEFT_DOWN
+ CHAIN_TOP_RIGHT_DOWN
+ CHAIN_BOTTOM_LEFT_UP
+ CHAIN_BOTTOM_RIGHT_UP
+ The location (i.e. 'TOP_LEFT', 'BOTTOM_RIGHT') etc. refers to the starting point where
+ the ESP32 is located, and how the chain of panels will 'roll out' from there.
+ In this example we're using 'CHAIN_BOTTOM_LEFT_UP' which would look like this in the real world:
-#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.
+ Chain of 4 x 64x32 panels with the ESP at the BOTTOM_LEFT:
-#define NUM_ROWS 2 // Number of rows of chained INDIVIDUAL PANELS
-#define NUM_COLS 2 // Number of INDIVIDUAL PANELS per ROW
-#define PANEL_CHAIN NUM_ROWS*NUM_COLS // total number of panels chained one to another
+ +---------+---------+
+ | 4 | 3 |
+ | | |
+ +---------+---------+
+ | 1 | 2 |
+ | (ESP) | |
+ +---------+---------+
-// 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_BOTTOM_LEFT_UP
-// library includes
-#include <ESP32-VirtualMatrixPanel-I2S-DMA.h>
+// 3) Create the runtime objects to use
-// placeholder for the matrix object
-MatrixPanel_I2S_DMA *dma_display = nullptr;
+ // placeholder for the matrix object
+ MatrixPanel_I2S_DMA *dma_display = nullptr;
-// placeholder for the virtual display object
-VirtualMatrixPanel *virtualDisp = nullptr;
+ // placeholder for the virtual display object
+ VirtualMatrixPanel *virtualDisp = nullptr;
/******************************************************************************
@@ -203,37 +112,58 @@ void setup() {
Serial.println("****** !KABOOM! I2S memory allocation failed ***********");
// create VirtualDisplay object based on our newly created dma_display object
- virtualDisp = new VirtualMatrixPanel((*dma_display), NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, SERPENT, TOPDOWN);
+ virtualDisp = new VirtualMatrixPanel((*dma_display), NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, VIRTUAL_MATRIX_CHAIN_TYPE);
// So far so good, so continue
virtualDisp->fillScreen(virtualDisp->color444(0, 0, 0));
virtualDisp->drawDisplayTest(); // draw text numbering on each screen to check connectivity
- delay(3000);
+ // delay(1000);
- Serial.println("Chain of 64x32 panels for this example:");
- Serial.println("+--------+---------+");
- Serial.println("| 4 | 3 |");
- Serial.println("| | |");
- Serial.println("+--------+---------+");
- Serial.println("| 1 | 2 |");
- Serial.println("| (ESP) | |");
- Serial.println("+--------+---------+");
+ Serial.println("Chain of 4x 64x32 panels for this example:");
+ Serial.println("+---------+---------+");
+ Serial.println("| 4 | 3 |");
+ Serial.println("| | |");
+ Serial.println("+---------+---------+");
+ Serial.println("| 1 | 2 |");
+ Serial.println("| (ESP32) | |");
+ Serial.println("+---------+---------+");
+ // draw blue text
virtualDisp->setFont(&FreeSansBold12pt7b);
virtualDisp->setTextColor(virtualDisp->color565(0, 0, 255));
- virtualDisp->setTextSize(2);
- virtualDisp->setCursor(10, virtualDisp->height()-20);
-
+ virtualDisp->setTextSize(3);
+ virtualDisp->setCursor(0, virtualDisp->height()- ((virtualDisp->height()-45)/2));
+ virtualDisp->print("ABCD");
+
// 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));
+
+ virtualDisp->drawDisplayTest(); // re draw text numbering on each screen to check connectivity
+
}
void loop() {
} // end loop
+
+
+/*****************************************************************************
+
+ Thanks to:
+
+ * Brian Lough for the original example as raised in this issue:
+ https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/26
+
+ YouTube: https://www.youtube.com/brianlough
+ Tindie: https://www.tindie.com/stores/brianlough/
+ Twitter: https://twitter.com/witnessmenow
+
+ * Galaxy-Man for the kind donation of panels make/test that this is possible:
+ https://github.com/Galaxy-Man
+
+*****************************************************************************/ \ No newline at end of file
diff --git a/examples/ChainedPanels/README.md b/examples/ChainedPanels/README.md
index ca55860..bf71095 100644
--- a/examples/ChainedPanels/README.md
+++ b/examples/ChainedPanels/README.md
@@ -10,12 +10,12 @@ Non-standard order is essentially the creation of a non-horizontal-only display
For example: You bought four (4) 64x32px panels, and wanted to use them to create a 128x64pixel display. You would use the VirtualMatrixPanel class.
-[Refer to this document](VirtualMatrixPanel.pdf) for an explanation and refer to this example on how to use.
+[Refer to this document](../doc/VirtualMatrixPanel.pdf) for an explanation and refer to this example on how to use.
### Steps to Use ###
-1. [Refer to this document](VirtualMatrixPanel.pdf) for an explanation and refer to this example on how to use.
+1. [Refer to this document](../doc/VirtualMatrixPanel.pdf) for an explanation and refer to this example on how to use.
2. In your Arduino sketch, configure these defines accordingly:
```
@@ -24,6 +24,11 @@ For example: You bought four (4) 64x32px panels, and wanted to use them to creat
#define NUM_ROWS 2 // Number of rows of chained INDIVIDUAL PANELS
#define NUM_COLS 2 // Number of INDIVIDUAL PANELS per ROW
+
+#define PANEL_CHAIN NUM_ROWS*NUM_COLS // total number of panels chained one to another
+
+#define VIRTUAL_MATRIX_CHAIN_TYPE <INSERT CHAINING TYPE HERE - Refer to documentation or example>
+
```
3. In your Arduino sketch, use the 'VirtualMatrixPanel' class instance (virtualDisp) to draw to the display (i.e. drawPixel), instead of the underling MatrixPanel_I2S_DMA class instance (dma_display).
diff --git a/examples/ChainedPanels/VirtualMatrixPanel.pdf b/examples/ChainedPanels/VirtualMatrixPanel.pdf
deleted file mode 100644
index db63112..0000000
--- a/examples/ChainedPanels/VirtualMatrixPanel.pdf
+++ /dev/null
Binary files differ
diff --git a/src/ESP32-VirtualMatrixPanel-I2S-DMA.h b/src/ESP32-VirtualMatrixPanel-I2S-DMA.h
index cf5324a..1f29a30 100644
--- a/src/ESP32-VirtualMatrixPanel-I2S-DMA.h
+++ b/src/ESP32-VirtualMatrixPanel-I2S-DMA.h
@@ -30,6 +30,8 @@
#include <Fonts/FreeSansBold12pt7b.h>
#endif
+#include <iostream>
+
struct VirtualCoords
{
int16_t x;
@@ -49,6 +51,15 @@ enum PANEL_SCAN_RATE
FOUR_SCAN_16PX_HIGH
};
+// Chaining approach... From the perspective of the DISPLAY / LED side of the chain of panels.
+enum PANEL_CHAIN_TYPE
+{
+ CHAIN_TOP_LEFT_DOWN,
+ CHAIN_TOP_RIGHT_DOWN,
+ CHAIN_BOTTOM_LEFT_UP,
+ CHAIN_BOTTOM_RIGHT_UP
+};
+
#ifdef USE_GFX_ROOT
class VirtualMatrixPanel : public GFX
#elif !defined NO_GFX
@@ -59,20 +70,8 @@ class VirtualMatrixPanel
{
public:
- int16_t virtualResX;
- int16_t virtualResY;
- int16_t vmodule_rows;
- int16_t vmodule_cols;
-
- 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;
-
- VirtualMatrixPanel(MatrixPanel_I2S_DMA &disp, int _vmodule_rows, int _vmodule_cols, int _panelResX, int _panelResY, bool serpentine_chain = true, bool top_down_chain = false)
+ VirtualMatrixPanel(MatrixPanel_I2S_DMA &disp, int _vmodule_rows, int _vmodule_cols, int _panelResX, int _panelResY, PANEL_CHAIN_TYPE _panel_chain_type = CHAIN_TOP_RIGHT_DOWN)
#ifdef USE_GFX_ROOT
: GFX(_vmodule_cols * _panelResX, _vmodule_rows * _panelResY)
#elif !defined NO_GFX
@@ -81,6 +80,8 @@ public:
{
this->display = &disp;
+ panel_chain_type = _panel_chain_type;
+
panelResX = _panelResX;
panelResY = _panelResY;
@@ -90,7 +91,7 @@ public:
virtualResX = vmodule_cols * _panelResX;
virtualResY = vmodule_rows * _panelResY;
- dmaResX = panelResX * vmodule_rows * vmodule_cols;
+ dmaResX = panelResX * vmodule_rows * vmodule_cols - 1;
/* Virtual Display width() and height() will return a real-world value. For example:
* Virtual Display width: 128
@@ -99,9 +100,6 @@ public:
* 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
}
@@ -129,15 +127,27 @@ public:
void setPhysicalPanelScanRate(PANEL_SCAN_RATE rate);
-protected:
+private:
+ MatrixPanel_I2S_DMA *display;
+
+ PANEL_CHAIN_TYPE panel_chain_type;
+ PANEL_SCAN_RATE panel_scan_rate = NORMAL_TWO_SCAN;
+
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;
+ int16_t virtualResX;
+ int16_t virtualResY;
+
+ int16_t vmodule_rows;
+ int16_t vmodule_cols;
+
+ int16_t panelResX;
+ int16_t panelResY;
- PANEL_SCAN_RATE _panelScanRate = NORMAL_TWO_SCAN;
+ int16_t dmaResX; // The width of the chain in pixels (as the DMA engine sees it)
+
+ bool _rotate = false;
}; // end Class header
@@ -146,71 +156,141 @@ protected:
* 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 &virt_x, int16_t &virt_y)
{
- // Serial.println("Called Base.");
- coords.x = coords.y = -1; // By defalt use an invalid co-ordinates that will be rejected by updateMatrixDMABuffer
-
- // Do we want to rotate?
- if (_rotate)
- {
- int16_t temp_x = x;
- x = y;
- y = virtualResY - 1 - temp_x;
- }
-
- if (x < 0 || x >= virtualResX || y < 0 || y >= virtualResY)
- { // Co-ordinates go from 0 to X-1 remember! otherwise they are out of range!
- // Serial.printf("VirtualMatrixPanel::getCoords(): Invalid virtual display coordinate. x,y: %d, %d\r\n", x, y);
- return coords;
- }
+ coords.x = coords.y = -1; // By defalt use an invalid co-ordinates that will be rejected by updateMatrixDMABuffer
- // Stupidity check
- if ((vmodule_rows == 1) && (vmodule_cols == 1)) // single panel...
- {
- coords.x = x;
- coords.y = y;
- }
- else
- {
- 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
- ||
- (_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;
+ if (virt_x < 0 || virt_x >= virtualResX || virt_y < 0 || virt_y >= virtualResY)
+ { // Co-ordinates go from 0 to X-1 remember! otherwise they are out of range!
+ return coords;
+ }
- // inverts the y the row
- coords.y = panelResY - 1 - (y % panelResY);
- }
- else
+ // Do we want to rotate?
+ if (_rotate)
{
- // Normal chain pixel co-ordinate
- coords.x = x + ((y / panelResY) * (virtualResX));
- coords.y = y % panelResY;
+ int16_t temp_x = virt_x;
+ virt_x = virt_y;
+ virt_y = virtualResY - 1 - temp_x;
}
- }
- // 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;
- */
- coords.x = (dmaResX - 1) - coords.x;
- coords.y = (panelResY - 1) - coords.y;
- }
+ int row = (virt_y / panelResY); // 0 indexed
+ switch(panel_chain_type)
+ {
+
+ case (CHAIN_TOP_RIGHT_DOWN):
+ {
+ if ( (row % 2) == 1 )
+ { // upside down panel
+
+ //Serial.printf("Condition 1, row %d ", row);
+
+ // refersed for the row
+ coords.x = dmaResX - virt_x - (row*virtualResX);
+
+ // y co-ord inverted within the panel
+ coords.y = panelResY - 1 - (virt_y % panelResY);
+
+
+ }
+ else
+ {
+ //Serial.printf("Condition 2, row %d ", row);
+
+ coords.x = ((vmodule_rows - (row+1))*virtualResX)+virt_x;
+ coords.y = virt_y % panelResY;
+
+ }
+
+ }
+ break;
+
+
+ case (CHAIN_TOP_LEFT_DOWN): // OK -> modulus opposite of CHAIN_TOP_RIGHT_DOWN
+ {
+ if ( (row % 2) == 0 )
+ { // refersed panel
+
+ //Serial.printf("Condition 1, row %d ", row);
+ coords.x = dmaResX - virt_x - (row*virtualResX);
+
+ // y co-ord inverted within the panel
+ coords.y = panelResY - 1 - (virt_y % panelResY);
+
+ }
+ else
+ {
+ //Serial.printf("Condition 2, row %d ", row);
+ coords.x = ((vmodule_rows - (row+1))*virtualResX)+virt_x;
+ coords.y = virt_y % panelResY;
+
+ }
+
+ }
+ break;
+
+
+
+
+ case (CHAIN_BOTTOM_LEFT_UP): //
+ {
+ row = vmodule_rows - row - 1;
+
+ if ( (row % 2) == 1 )
+ {
+ // Serial.printf("Condition 1, row %d ", row);
+ coords.x = ((vmodule_rows - (row+1))*virtualResX)+virt_x;
+ coords.y = virt_y % panelResY;
+
+ }
+ else
+ { // inverted panel
+
+ // Serial.printf("Condition 2, row %d ", row);
+ coords.x = dmaResX - (row*virtualResX) - virt_x;
+ coords.y = panelResY - 1 - (virt_y % panelResY);
+ }
+
+ }
+ break;
+
+ case (CHAIN_BOTTOM_RIGHT_UP): // OK -> modulus opposite of CHAIN_BOTTOM_LEFT_UP
+ {
+ row = vmodule_rows - row - 1;
+
+ if ( (row % 2) == 0 )
+ { // right side up
+
+ // Serial.printf("Condition 1, row %d ", row);
+ // refersed for the row
+ coords.x = ((vmodule_rows - (row+1))*virtualResX)+virt_x;
+ coords.y = virt_y % panelResY;
+
+ }
+ else
+ { // inverted panel
+
+ // Serial.printf("Condition 2, row %d ", row);
+ coords.x = dmaResX - (row*virtualResX) - virt_x;
+ coords.y = panelResY - 1 - (virt_y % panelResY);
+ }
+
+ }
+ break;
+
+
+ default:
+ return coords;
+ break;
+
+ } // end switch
+
+
/* START: Pixel remapping AGAIN to convert TWO parallel scanline output that the
* the underlying hardware library is designed for (because
* there's only 2 x RGB pins... and convert this to 1/4 or something
*/
- if (_panelScanRate == FOUR_SCAN_32PX_HIGH)
+ if (panel_scan_rate == FOUR_SCAN_32PX_HIGH)
{
/* 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
@@ -221,12 +301,7 @@ inline VirtualCoords VirtualMatrixPanel::getCoords(int16_t &x, int16_t &y)
as if the panel is 2 * W and 0.5 * H !
*/
- /*
- 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)
+ if ((virt_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
}
@@ -238,17 +313,12 @@ inline VirtualCoords VirtualMatrixPanel::getCoords(int16_t &x, int16_t &y)
// 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);
+ coords.y = (virt_y >> 4) * 8 + (virt_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(") ");
- */
}
- else if (_panelScanRate == FOUR_SCAN_16PX_HIGH)
+ else if (panel_scan_rate == FOUR_SCAN_16PX_HIGH)
{
- if ((y & 8) == 0)
+ if ((virt_y & 8) == 0)
{
coords.x += (panelResX >> 2) * (((coords.x & 0xFFF0) >> 4) + 1); // 1st, 3rd 'block' of 8 rows of pixels, offset by panel width in DMA buffer
}
@@ -257,22 +327,22 @@ inline VirtualCoords VirtualMatrixPanel::getCoords(int16_t &x, int16_t &y)
coords.x += (panelResX >> 2) * (((coords.x & 0xFFF0) >> 4)); // 2nd, 4th 'block' of 8 rows of pixels, offset by panel width in DMA buffer
}
- if (y < 32)
- coords.y = (y >> 4) * 8 + (y & 0b00000111);
+ if (virt_y < 32)
+ coords.y = (virt_y >> 4) * 8 + (virt_y & 0b00000111);
else
{
- coords.y = ((y - 32) >> 4) * 8 + (y & 0b00000111);
+ coords.y = ((virt_y - 32) >> 4) * 8 + (virt_y & 0b00000111);
coords.x += 256;
}
}
- // Serial.print("Mapping to x: "); Serial.print(coords.x, DEC); Serial.print(", y: "); Serial.println(coords.y, DEC);
return coords;
}
inline void VirtualMatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t color)
{ // adafruit virtual void override
getCoords(x, y);
+ // Serial.printf("Requested virtual x,y coord (%d, %d), got phyical chain coord of (%d,%d)\n", x,y, coords.x, coords.y);
this->display->drawPixel(coords.x, coords.y, color);
}
@@ -325,7 +395,7 @@ inline void VirtualMatrixPanel::setRotate(bool rotate)
inline void VirtualMatrixPanel::setPhysicalPanelScanRate(PANEL_SCAN_RATE rate)
{
- _panelScanRate = rate;
+ panel_scan_rate = rate;
}
#ifndef NO_GFX
@@ -339,7 +409,7 @@ inline void VirtualMatrixPanel::drawDisplayTest()
{
int top_left_x = (panel == 0) ? 0 : (panel * panelResX);
this->display->drawRect(top_left_x, 0, panelResX, panelResY, this->display->color565(0, 255, 0));
- this->display->setCursor(panel * panelResX, panelResY - 3);
+ this->display->setCursor( (panel * panelResX)+2, panelResY - 4);
this->display->print((vmodule_cols * vmodule_rows) - panel);
}
}
diff --git a/src/ESP32-VirtualMatrixPanel-I2S-DMA.h-old b/src/ESP32-VirtualMatrixPanel-I2S-DMA.h-old
new file mode 100644
index 0000000..cf5324a
--- /dev/null
+++ b/src/ESP32-VirtualMatrixPanel-I2S-DMA.h-old
@@ -0,0 +1,363 @@
+#ifndef _ESP32_VIRTUAL_MATRIX_PANEL_I2S_DMA
+#define _ESP32_VIRTUAL_MATRIX_PANEL_I2S_DMA
+
+/*******************************************************************
+ Class contributed by Brian Lough, and expanded by Faptastic.
+
+ Originally designed to allow CHAINING of panels together to create
+ a 'bigger' display of panels. i.e. Chaining 4 panels into a 2x2
+ grid.
+
+ However, the function of this class has expanded now to also manage
+ the output for
+
+ 1) TWO scan panels = Two rows updated in parallel.
+ * 64px high panel = sometimes referred to as 1/32 scan
+ * 32px high panel = sometimes referred to as 1/16 scan
+ * 16px high panel = sometimes referred to as 1/8 scan
+
+ 2) FOUR scan panels = Four rows updated in parallel
+ * 32px high panel = sometimes referred to as 1/8 scan
+ * 16px high panel = sometimes referred to as 1/4 scan
+
+ YouTube: https://www.youtube.com/brianlough
+ Tindie: https://www.tindie.com/stores/brianlough/
+ Twitter: https://twitter.com/witnessmenow
+ *******************************************************************/
+
+#include "ESP32-HUB75-MatrixPanel-I2S-DMA.h"
+#ifndef NO_GFX
+#include <Fonts/FreeSansBold12pt7b.h>
+#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)
+ {
+ }
+};
+
+enum PANEL_SCAN_RATE
+{
+ NORMAL_TWO_SCAN, NORMAL_ONE_SIXTEEN, // treated as the same
+ FOUR_SCAN_32PX_HIGH,
+ FOUR_SCAN_16PX_HIGH
+};
+
+#ifdef USE_GFX_ROOT
+class VirtualMatrixPanel : public GFX
+#elif !defined NO_GFX
+class VirtualMatrixPanel : public Adafruit_GFX
+#else
+class VirtualMatrixPanel
+#endif
+{
+
+public:
+ int16_t virtualResX;
+ int16_t virtualResY;
+
+ int16_t vmodule_rows;
+ int16_t vmodule_cols;
+
+ 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;
+
+ VirtualMatrixPanel(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;
+
+ dmaResX = panelResX * vmodule_rows * vmodule_cols;
+
+ /* 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
+ }
+
+ // 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
+ virtual void fillScreenRGB888(uint8_t r, uint8_t g, uint8_t b);
+
+ void clearScreen() { display->clearScreen(); }
+ void drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b);
+
+#ifdef USE_GFX_ROOT
+ // 24bpp FASTLED CRGB colour struct support
+ void fillScreen(CRGB color);
+ void drawPixel(int16_t x, int16_t y, CRGB color);
+#endif
+
+ 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 drawDisplayTest();
+ void setRotate(bool rotate);
+
+ void setPhysicalPanelScanRate(PANEL_SCAN_RATE rate);
+
+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;
+
+ PANEL_SCAN_RATE _panelScanRate = NORMAL_TWO_SCAN;
+
+}; // end Class header
+
+/**
+ * Calculate virtual->real co-ordinate mapping to underlying single chain of panels connected to ESP32.
+ * 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)
+{
+ // Serial.println("Called Base.");
+ coords.x = coords.y = -1; // By defalt use an invalid co-ordinates that will be rejected by updateMatrixDMABuffer
+
+ // Do we want to rotate?
+ if (_rotate)
+ {
+ int16_t temp_x = x;
+ x = y;
+ y = virtualResY - 1 - temp_x;
+ }
+
+ if (x < 0 || x >= virtualResX || y < 0 || y >= virtualResY)
+ { // Co-ordinates go from 0 to X-1 remember! otherwise they are out of range!
+ // Serial.printf("VirtualMatrixPanel::getCoords(): Invalid virtual display coordinate. x,y: %d, %d\r\n", x, y);
+ return coords;
+ }
+
+ // Stupidity check
+ if ((vmodule_rows == 1) && (vmodule_cols == 1)) // single panel...
+ {
+ coords.x = x;
+ coords.y = y;
+ }
+ else
+ {
+ 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
+ ||
+ (_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;
+ }
+ }
+
+ // 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;
+ */
+ coords.x = (dmaResX - 1) - coords.x;
+ coords.y = (panelResY - 1) - coords.y;
+ }
+
+ /* START: Pixel remapping AGAIN to convert TWO parallel scanline output that the
+ * the underlying hardware library is designed for (because
+ * there's only 2 x RGB pins... and convert this to 1/4 or something
+ */
+ if (_panelScanRate == FOUR_SCAN_32PX_HIGH)
+ {
+ /* 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 FourScanPanel example code and you'll see that the DMA buffer is setup
+ as if the panel is 2 * W and 0.5 * H !
+ */
+
+ /*
+ 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(") ");
+ */
+ }
+ else if (_panelScanRate == FOUR_SCAN_16PX_HIGH)
+ {
+ if ((y & 8) == 0)
+ {
+ coords.x += (panelResX >> 2) * (((coords.x & 0xFFF0) >> 4) + 1); // 1st, 3rd 'block' of 8 rows of pixels, offset by panel width in DMA buffer
+ }
+ else
+ {
+ coords.x += (panelResX >> 2) * (((coords.x & 0xFFF0) >> 4)); // 2nd, 4th 'block' of 8 rows of pixels, offset by panel width in DMA buffer
+ }
+
+ if (y < 32)
+ coords.y = (y >> 4) * 8 + (y & 0b00000111);
+ else
+ {
+ coords.y = ((y - 32) >> 4) * 8 + (y & 0b00000111);
+ coords.x += 256;
+ }
+ }
+
+ // Serial.print("Mapping to x: "); Serial.print(coords.x, DEC); Serial.print(", y: "); Serial.println(coords.y, DEC);
+ return coords;
+}
+
+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
+ this->display->fillScreen(color);
+}
+
+inline void VirtualMatrixPanel::fillScreenRGB888(uint8_t r, uint8_t g, uint8_t b)
+{
+ this->display->fillScreenRGB888(r, g, b);
+}
+
+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);
+}
+
+#ifdef USE_GFX_ROOT
+// Support for CRGB values provided via FastLED
+inline void VirtualMatrixPanel::drawPixel(int16_t x, int16_t y, CRGB color)
+{
+ getCoords(x, y);
+ this->display->drawPixel(coords.x, coords.y, color);
+}
+
+inline void VirtualMatrixPanel::fillScreen(CRGB color)
+{
+ this->display->fillScreen(color);
+}
+#endif
+
+inline void VirtualMatrixPanel::setRotate(bool rotate)
+{
+ _rotate = rotate;
+
+#ifndef NO_GFX
+ // We don't support rotation by degrees.
+ if (rotate)
+ {
+ setRotation(1);
+ }
+ else
+ {
+ setRotation(0);
+ }
+#endif
+}
+
+inline void VirtualMatrixPanel::setPhysicalPanelScanRate(PANEL_SCAN_RATE rate)
+{
+ _panelScanRate = rate;
+}
+
+#ifndef NO_GFX
+inline void VirtualMatrixPanel::drawDisplayTest()
+{
+ this->display->setFont(&FreeSansBold12pt7b);
+ this->display->setTextColor(this->display->color565(255, 255, 0));
+ this->display->setTextSize(1);
+
+ for (int panel = 0; panel < vmodule_cols * vmodule_rows; panel++)
+ {
+ int top_left_x = (panel == 0) ? 0 : (panel * panelResX);
+ this->display->drawRect(top_left_x, 0, panelResX, panelResY, this->display->color565(0, 255, 0));
+ this->display->setCursor(panel * panelResX, panelResY - 3);
+ this->display->print((vmodule_cols * vmodule_rows) - panel);
+ }
+}
+#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;
+ for (i = 0; i < icon_rows; i++) {
+ for (j = 0; j < icon_cols; j++) {
+ // This is a call to this libraries version of drawPixel
+ // which will map each pixel, which is what we want.
+ //drawPixelRGB565 (x + j, y + i, ico[i * module_cols + j]);
+ drawPixel (x + j, y + i, ico[i * icon_cols + j]);
+ }
+ }
+}
+*/
+
+#endif