Improvement to ensure DMA payload limit not hit.
This commit is contained in:
parent
80d5ddff4c
commit
3159202ccf
5 changed files with 216 additions and 20 deletions
|
@ -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)
|
||||
|
@ -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,18 +327,48 @@ 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);
|
||||
#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);
|
||||
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
|
||||
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
134
ESP32-VirtualMatrixPanel-I2S-DMA.h
Normal file
134
ESP32-VirtualMatrixPanel-I2S-DMA.h
Normal file
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue