aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormrfaptastic <12006953+mrfaptastic@users.noreply.github.com>2020-08-02 22:03:16 +0100
committermrfaptastic <12006953+mrfaptastic@users.noreply.github.com>2020-08-02 22:03:16 +0100
commit3159202ccfb58c971ff61aa0ec1bf771cc72983a (patch)
tree8bfd958b2c5f7a301985ff8fb04a4719b49fef2a
parent80d5ddff4c01564ea020b8f6ab00a1a3224f0fe3 (diff)
Improvement to ensure DMA payload limit not hit.
-rw-r--r--ESP32-RGB64x32MatrixPanel-I2S-DMA.cpp92
-rw-r--r--ESP32-RGB64x32MatrixPanel-I2S-DMA.h2
-rw-r--r--ESP32-VirtualMatrixPanel-I2S-DMA.h134
-rw-r--r--esp32_i2s_parallel.c5
-rw-r--r--esp32_i2s_parallel.h3
5 files changed, 216 insertions, 20 deletions
diff --git a/ESP32-RGB64x32MatrixPanel-I2S-DMA.cpp b/ESP32-RGB64x32MatrixPanel-I2S-DMA.cpp
index c62f6e8..91f3e81 100644
--- a/ESP32-RGB64x32MatrixPanel-I2S-DMA.cpp
+++ b/ESP32-RGB64x32MatrixPanel-I2S-DMA.cpp
@@ -156,7 +156,7 @@ bool RGB64x32MatrixPanel_I2S_DMA::allocateDMAmemory()
int ramrequired = numDMAdescriptorsPerRow * ROWS_PER_FRAME * _num_frame_buffers * sizeof(lldesc_t);
int largestblockfree = heap_caps_get_largest_free_block(MALLOC_CAP_DMA);
#if SERIAL_DEBUG
- Serial.printf("numdesciptors per row %d, lsbMsbTransitionBit of %d requires %d RAM, %d available, leaving %d free: \r\n", numDMAdescriptorsPerRow, lsbMsbTransitionBit, ramrequired, largestblockfree, largestblockfree - ramrequired);
+ Serial.printf("lsbMsbTransitionBit of %d with %d DMA descriptors per frame row, requires %d bytes RAM, %d available, leaving %d free: \r\n", lsbMsbTransitionBit, numDMAdescriptorsPerRow, ramrequired, largestblockfree, largestblockfree - ramrequired);
#endif
if(ramrequired < largestblockfree)
@@ -187,8 +187,8 @@ bool RGB64x32MatrixPanel_I2S_DMA::allocateDMAmemory()
calculated_refresh_rate = actualRefreshRate;
#if SERIAL_DEBUG
- Serial.printf("lsbMsbTransitionBit of %d gives %d Hz refresh: \r\n", lsbMsbTransitionBit, actualRefreshRate);
- #endif
+ Serial.printf("lsbMsbTransitionBit of %d gives %d Hz refresh: \r\n", lsbMsbTransitionBit, actualRefreshRate);
+ #endif
if (actualRefreshRate > min_refresh_rate) // HACK Hard Coded: 100
break;
@@ -201,19 +201,41 @@ bool RGB64x32MatrixPanel_I2S_DMA::allocateDMAmemory()
Serial.printf("Raised lsbMsbTransitionBit to %d/%d to meet minimum refresh rate\r\n", lsbMsbTransitionBit, PIXEL_COLOR_DEPTH_BITS - 1);
- // lsbMsbTransition Bit is now finalized - recalcuate descriptor count in case it changed to hit min refresh rate
+ /***
+ * Step 2a: lsbMsbTransition bit is now finalised - recalculate the DMA descriptor count required, which is used for
+ * memory allocation of the DMA linked list memory structure.
+ */
numDMAdescriptorsPerRow = 1;
for(int i=lsbMsbTransitionBit + 1; i<PIXEL_COLOR_DEPTH_BITS; i++) {
numDMAdescriptorsPerRow += 1<<(i - lsbMsbTransitionBit - 1);
}
+ //Serial.printf("Size of (sizeof(rowBitStruct) * PIXEL_COLOR_DEPTH_BITS): %d.\r\n", (sizeof(rowBitStruct) * PIXEL_COLOR_DEPTH_BITS));
+ //Serial.printf("Size of sizeof(rowColorDepthStruct): %d.\r\n", sizeof(rowColorDepthStruct));
+
+
+ // Going to need a little more DMA LL memory structure RAM.
+ // Refer to 'DMA_LL_PAYLOAD_SPLIT' code in configureDMA() below to understand why this exists.
+ // numDMAdescriptorsPerRow is also used to calcaulte descount which is super important in i2s_parallel_config_t SoC DMA setup.
+ if ( sizeof(rowColorDepthStruct) > DMA_MAX ) {
+
+ #if SERIAL_DEBUG
+ Serial.println("Split DMA payload enabled. Increasing DMA descriptor count per frame row by one.");
+ #endif
+
+ //numDMAdescriptorsPerRow += 1;
+ numDMAdescriptorsPerRow += PIXEL_COLOR_DEPTH_BITS-1;
+ // Not if numDMAdescriptorsPerRow is even just one descriptor too large, DMA linked list will not correctly loop.
+ }
+
+
/***
* Step 3: Allocate memory for DMA linked list, linking up each framebuffer row in sequence for GPIO output.
*/
_dma_linked_list_memory_required = numDMAdescriptorsPerRow * ROWS_PER_FRAME * _num_frame_buffers * sizeof(lldesc_t);
#if SERIAL_DEBUG
- Serial.printf("Descriptors for lsbMsbTransitionBit %d/%d with %d rows require %d bytes of DMA RAM\r\n", lsbMsbTransitionBit, PIXEL_COLOR_DEPTH_BITS - 1, ROWS_PER_FRAME, _dma_linked_list_memory_required);
+ Serial.printf("Descriptors for lsbMsbTransitionBit of %d/%d with %d frame rows require %d bytes of DMA RAM with %d numDMAdescriptorsPerRow.\r\n", lsbMsbTransitionBit, PIXEL_COLOR_DEPTH_BITS - 1, ROWS_PER_FRAME, _dma_linked_list_memory_required, numDMAdescriptorsPerRow);
#endif
_total_dma_capable_memory_reserved += _dma_linked_list_memory_required;
@@ -279,11 +301,18 @@ void RGB64x32MatrixPanel_I2S_DMA::configureDMA(int r1_pin, int g1_pin, int b1_
lldesc_t *previous_dmadesc_b = 0;
int current_dmadescriptor_offset = 0;
+ // HACK: If we need to split the payload in 1/2 so that it doesn't breach DMA_MAX, lets do it by the color_depth.
+ // We also declare a ridiculously long variable as well... row_bit_struct_color_depth_dma_payload_break_point
+ int num_dma_payload_color_depths = PIXEL_COLOR_DEPTH_BITS;
+ if ( sizeof(rowColorDepthStruct) > DMA_MAX ) {
+ num_dma_payload_color_depths = 1;
+ }
+
/* Fill DMA linked lists for both frames (as in, halves of the HUB75 panel)
* .. and if double buffering is enabled, link it up for both buffers.
*/
- for(int j = 0; j < ROWS_PER_FRAME; j++)
- {
+ for(int j = 0; j < ROWS_PER_FRAME; j++) {
+
// Split framebuffer malloc hack 'improvement'
frameStruct *fb_malloc_ptr = matrix_framebuffer_malloc_1;
int fb_malloc_j = j;
@@ -298,25 +327,55 @@ void RGB64x32MatrixPanel_I2S_DMA::configureDMA(int r1_pin, int g1_pin, int b1_
}
#endif
- // first set of data is LSB through MSB, single pass - all color bits are displayed once, which takes care of everything below and inlcluding LSBMSB_TRANSITION_BIT
- // TODO: size must be less than DMA_MAX - worst case for SmartMatrix Library: 16-bpp with 256 pixels per row would exceed this, need to break into two
- link_dma_desc(&dmadesc_a[current_dmadescriptor_offset], previous_dmadesc_a, &(fb_malloc_ptr[0].rowdata[fb_malloc_j].rowbits[0].data), sizeof(rowBitStruct) * PIXEL_COLOR_DEPTH_BITS);
- previous_dmadesc_a = &dmadesc_a[current_dmadescriptor_offset];
+ #if SERIAL_DEBUG
+ Serial.printf("DMA payload of %d bytes. DMA_MAX is %d.\r\n", sizeof(rowBitStruct) * PIXEL_COLOR_DEPTH_BITS, DMA_MAX);
+ #endif
+
+
+ // first set of data is LSB through MSB, single pass (IF TOTAL SIZE < DMA_MAX) - all color bits are displayed once, which takes care of everything below and inlcluding LSBMSB_TRANSITION_BIT
+ // NOTE: size must be less than DMA_MAX - worst case for library: 16-bpp with 256 pixels per row would exceed this, need to break into two
+ link_dma_desc(&dmadesc_a[current_dmadescriptor_offset], previous_dmadesc_a, &(fb_malloc_ptr[0].rowdata[fb_malloc_j].rowbits[0].data), sizeof(rowBitStruct) * num_dma_payload_color_depths);
+ previous_dmadesc_a = &dmadesc_a[current_dmadescriptor_offset];
if (double_buffering_enabled) {
- link_dma_desc(&dmadesc_b[current_dmadescriptor_offset], previous_dmadesc_b, &(fb_malloc_ptr[1].rowdata[fb_malloc_j].rowbits[0].data), sizeof(rowBitStruct) * PIXEL_COLOR_DEPTH_BITS);
- previous_dmadesc_b = &dmadesc_b[current_dmadescriptor_offset]; }
-
+ link_dma_desc(&dmadesc_b[current_dmadescriptor_offset], previous_dmadesc_b, &(fb_malloc_ptr[1].rowdata[fb_malloc_j].rowbits[0].data), sizeof(rowBitStruct) * num_dma_payload_color_depths);
+ previous_dmadesc_b = &dmadesc_b[current_dmadescriptor_offset]; }
+
current_dmadescriptor_offset++;
+ // If the number of pixels per row is to great for the size of a single payload, so we need to split what we were going to send above.
+ if ( sizeof(rowColorDepthStruct) > DMA_MAX ) {
+
+ #if SERIAL_DEBUG
+ Serial.printf("Spliting DMA payload for %d color depths into %d byte payloads.\r\n", PIXEL_COLOR_DEPTH_BITS-1, sizeof(rowBitStruct) );
+ #endif
+
+ for (int cd = 1; cd < PIXEL_COLOR_DEPTH_BITS; cd++) {
+
+ // first set of data is LSB through MSB, single pass - all color bits are displayed once, which takes care of everything below and inlcluding LSBMSB_TRANSITION_BIT
+ // TODO: size must be less than DMA_MAX - worst case for library: 16-bpp with 256 pixels per row would exceed this, need to break into two
+ link_dma_desc(&dmadesc_a[current_dmadescriptor_offset], previous_dmadesc_a, &(fb_malloc_ptr[0].rowdata[fb_malloc_j].rowbits[cd].data), sizeof(rowBitStruct) );
+ previous_dmadesc_a = &dmadesc_a[current_dmadescriptor_offset];
+
+ if (double_buffering_enabled) {
+ link_dma_desc(&dmadesc_b[current_dmadescriptor_offset], previous_dmadesc_b, &(fb_malloc_ptr[1].rowdata[fb_malloc_j].rowbits[cd].data), sizeof(rowBitStruct) );
+ previous_dmadesc_b = &dmadesc_b[current_dmadescriptor_offset]; }
+
+ current_dmadescriptor_offset++;
+ } // additional linked list items
+
+ } // row depth struct
+
+
for(int i=lsbMsbTransitionBit + 1; i<PIXEL_COLOR_DEPTH_BITS; i++) {
+
// binary time division setup: we need 2 of bit (LSBMSB_TRANSITION_BIT + 1) four of (LSBMSB_TRANSITION_BIT + 2), etc
// because we sweep through to MSB each time, it divides the number of times we have to sweep in half (saving linked list RAM)
// we need 2^(i - LSBMSB_TRANSITION_BIT - 1) == 1 << (i - LSBMSB_TRANSITION_BIT - 1) passes from i to MSB
//Serial.printf("buffer %d: repeat %d times, size: %d, from %d - %d\r\n", current_dmadescriptor_offset, 1<<(i - lsbMsbTransitionBit - 1), (PIXEL_COLOR_DEPTH_BITS - i), i, PIXEL_COLOR_DEPTH_BITS-1);
for(int k=0; k < 1<<(i - lsbMsbTransitionBit - 1); k++) {
-
+
link_dma_desc(&dmadesc_a[current_dmadescriptor_offset], previous_dmadesc_a, &(fb_malloc_ptr[0].rowdata[fb_malloc_j].rowbits[i].data), sizeof(rowBitStruct) * (PIXEL_COLOR_DEPTH_BITS - i));
previous_dmadesc_a = &dmadesc_a[current_dmadescriptor_offset];
@@ -328,10 +387,11 @@ void RGB64x32MatrixPanel_I2S_DMA::configureDMA(int r1_pin, int g1_pin, int b1_
} // end color depth ^ 2 linked list
} // end color depth loop
+
} // end frame rows
#if SERIAL_DEBUG
- Serial.println("configureDMA(): Configured LL structure.\r\n");
+ Serial.printf("configureDMA(): Configured LL structure. %d DMA Linked List descriptors populated.\r\n", current_dmadescriptor_offset);
#endif
dmadesc_a[desccount-1].eof = 1;
diff --git a/ESP32-RGB64x32MatrixPanel-I2S-DMA.h b/ESP32-RGB64x32MatrixPanel-I2S-DMA.h
index 1850839..1cc5931 100644
--- a/ESP32-RGB64x32MatrixPanel-I2S-DMA.h
+++ b/ESP32-RGB64x32MatrixPanel-I2S-DMA.h
@@ -15,7 +15,7 @@
#define SPLIT_MEMORY_MODE 1
/* Use GFX_Root (https://github.com/mrfaptastic/GFX_Root) instead of
- * Adafruit_GFX library. No real benefit unless you don't want Bus_IO library.
+ * Adafruit_GFX library. No real benefit unless you don't want Bus_IO & Wire.h library dependencies.
*/
//#define USE_GFX_ROOT 1
diff --git a/ESP32-VirtualMatrixPanel-I2S-DMA.h b/ESP32-VirtualMatrixPanel-I2S-DMA.h
new file mode 100644
index 0000000..c42b962
--- /dev/null
+++ b/ESP32-VirtualMatrixPanel-I2S-DMA.h
@@ -0,0 +1,134 @@
+#ifndef _ESP32_VIRTUAL_MATRIX_PANEL_I2S_DMA
+#define _ESP32_VIRTUAL_MATRIX_PANEL_I2S_DMA
+
+/*******************************************************************
+ Contributed by Brian Lough
+ YouTube: https://www.youtube.com/brianlough
+ Tindie: https://www.tindie.com/stores/brianlough/
+ Twitter: https://twitter.com/witnessmenow
+ *******************************************************************/
+
+#include "ESP32-RGB64x32MatrixPanel-I2S-DMA.h"
+
+struct VirtualCoords {
+ int16_t x;
+ int16_t y;
+};
+
+
+#ifdef USE_GFX_ROOT
+class VirtualMatrixPanel : public GFX
+#else
+class VirtualMatrixPanel : public Adafruit_GFX
+#endif
+{
+
+ public:
+ int16_t virtualResX;
+ int16_t virtualResY;
+
+ int16_t module_rows;
+ int16_t module_cols;
+
+ int16_t screenResX;
+ int16_t screenResY;
+
+ RGB64x32MatrixPanel_I2S_DMA *display;
+
+#ifdef USE_GFX_ROOT
+ VirtualMatrixPanel(RGB64x32MatrixPanel_I2S_DMA &disp, int vmodule_rows, int vmodule_cols, int screenX, int screenY)
+ : GFX(vmodule_cols*screenX, vmodule_rows*screenY)
+#else
+ VirtualMatrixPanel(RGB64x32MatrixPanel_I2S_DMA &disp, int vmodule_rows, int vmodule_cols, int screenX, int screenY)
+ : Adafruit_GFX(vmodule_cols*screenX, vmodule_rows*screenY)
+#endif
+ {
+ this->display = &disp;
+ module_rows = vmodule_rows;
+ module_cols = vmodule_cols;
+ screenResX = screenX;
+ screenResY = screenY;
+
+ virtualResX = vmodule_rows*screenY;
+ virtualResY = vmodule_cols*screenX;
+ }
+
+ VirtualCoords getCoords(int16_t x, int16_t y);
+
+ // 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
+ void clearScreen() {
+ fillScreen(0);
+ }
+ void drawPixelRGB565(int16_t x, int16_t y, uint16_t color);
+ void drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b);
+ void drawPixelRGB24(int16_t x, int16_t y, rgb_24 color);
+ void drawIcon (int *ico, int16_t x, int16_t y, int16_t module_cols, int16_t module_rows);
+
+ 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);
+ }
+
+ private:
+ VirtualCoords coords;
+
+}; // end Class header
+
+inline VirtualCoords VirtualMatrixPanel::getCoords(int16_t x, int16_t y) {
+ int16_t xOffset = (y / screenResY) * (module_cols * screenResX);
+ coords.x = x + xOffset;
+ coords.y = y % screenResY;
+ return coords;
+}
+
+inline void VirtualMatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t color)
+{
+ VirtualCoords coords = getCoords(x, y);
+ this->display->drawPixel(coords.x, coords.y, color);
+}
+
+inline void VirtualMatrixPanel::fillScreen(uint16_t color) // adafruit virtual void override
+{
+ // No need to map this.
+ this->display->fillScreen(color);
+}
+
+inline void VirtualMatrixPanel::drawPixelRGB565(int16_t x, int16_t y, uint16_t color)
+{
+ VirtualCoords coords = getCoords(x, y);
+ this->display->drawPixelRGB565( coords.x, coords.y, color);
+}
+
+
+inline void VirtualMatrixPanel::drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b)
+{
+ VirtualCoords coords = getCoords(x, y);
+ this->display->drawPixelRGB888( coords.x, coords.y, r, g, b);
+}
+
+inline void VirtualMatrixPanel::drawPixelRGB24(int16_t x, int16_t y, rgb_24 color)
+{
+ VirtualCoords coords = getCoords(x, y);
+ this->display->drawPixelRGB24(coords.x, coords.y, color);
+}
+
+// need to recreate this one, as it wouldnt work to just map where it starts.
+inline void VirtualMatrixPanel::drawIcon (int *ico, int16_t x, int16_t y, int16_t module_cols, int16_t module_rows) {
+ int i, j;
+ for (i = 0; i < module_rows; i++) {
+ for (j = 0; j < module_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]);
+ }
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/esp32_i2s_parallel.c b/esp32_i2s_parallel.c
index 52a3adc..f45fb96 100644
--- a/esp32_i2s_parallel.c
+++ b/esp32_i2s_parallel.c
@@ -32,8 +32,6 @@
//#include "esp_heap_caps.h"
#include "esp32_i2s_parallel.h"
-#define DMA_MAX (4096-4)
-
typedef struct {
volatile lldesc_t *dmadesc_a, *dmadesc_b;
int desccount_a, desccount_b;
@@ -106,7 +104,8 @@ static void fill_dma_desc(volatile lldesc_t *dmadesc, i2s_parallel_buffer_desc_t
// size must be less than DMA_MAX - need to handle breaking long transfer into two descriptors before call
// DMA_MAX by the way is the maximum data packet size you can hold in one chunk
-void link_dma_desc(volatile lldesc_t *dmadesc, volatile lldesc_t *prevdmadesc, void *memory, size_t size) {
+void link_dma_desc(volatile lldesc_t *dmadesc, volatile lldesc_t *prevdmadesc, void *memory, size_t size)
+{
if(size > DMA_MAX) size = DMA_MAX;
dmadesc->size = size;
diff --git a/esp32_i2s_parallel.h b/esp32_i2s_parallel.h
index beb59d8..d012422 100644
--- a/esp32_i2s_parallel.h
+++ b/esp32_i2s_parallel.h
@@ -12,6 +12,9 @@ extern "C" {
#include "soc/i2s_struct.h"
#include "rom/lldesc.h"
+#define DMA_MAX (4096-4)
+//#define DMA_MAX (512)
+
typedef enum {
I2S_PARALLEL_BITS_8=8,
I2S_PARALLEL_BITS_16=16,