Improved memory checks

This commit is contained in:
mrfaptastic 2020-07-29 10:44:38 +01:00
parent 75136c59e1
commit 9a5ed4db9e
5 changed files with 187 additions and 173 deletions

View file

@ -76,10 +76,10 @@ bool RGB64x32MatrixPanel_I2S_DMA::allocateDMAmemory()
if (double_buffering_enabled) { if (double_buffering_enabled) {
Serial.println("DOUBLE FRAME BUFFERS / DOUBLE BUFFERING IS ENABLED. DOUBLE THE RAM REQUIRED!"); Serial.println("DOUBLE FRAME BUFFERS / DOUBLE BUFFERING IS ENABLED. DOUBLE THE RAM REQUIRED!");
} }
/*
Serial.println("DMA memory blocks available before any malloc's: "); Serial.println("DMA memory blocks available before any malloc's: ");
heap_caps_print_heap_info(MALLOC_CAP_DMA); heap_caps_print_heap_info(MALLOC_CAP_DMA);
*/
Serial.printf("FYI: Size of an ESP32 DMA linked list descriptor (lldesc_t) is %d bytes\r\n", sizeof(lldesc_t)); Serial.printf("FYI: Size of an ESP32 DMA linked list descriptor (lldesc_t) is %d bytes\r\n", sizeof(lldesc_t));
Serial.printf("We're going to need %d bytes of SRAM just for the frame buffer(s).\r\n", _frame_buffer_memory_required); Serial.printf("We're going to need %d bytes of SRAM just for the frame buffer(s).\r\n", _frame_buffer_memory_required);
@ -87,37 +87,53 @@ bool RGB64x32MatrixPanel_I2S_DMA::allocateDMAmemory()
#endif #endif
// Can we fit the framebuffer into the single DMA capable memory block available? // Can we fit the framebuffer into the single DMA capable memory block available?
if ( heap_caps_get_largest_free_block(MALLOC_CAP_DMA) >= _frame_buffer_memory_required ) { // YES - SIMPLE if ( heap_caps_get_largest_free_block(MALLOC_CAP_DMA) < _frame_buffer_memory_required ) {
// Allocate the framebuffer memory, fail if we can even do this
matrix_framebuffer_malloc_1 = (frameStruct *)heap_caps_malloc(_frame_buffer_memory_required, MALLOC_CAP_DMA);
if ( !matrix_framebuffer_malloc_1 ) {
#if SERIAL_DEBUG
Serial.println("ERROR: Couldn't malloc matrix_framebuffer_malloc_1! Critical fail.\r\n");
#endif
return false;
}
_total_dma_capable_memory_reserved += _frame_buffer_memory_required;
}
#ifdef SPLIT_MEMORY_MODE
Serial.println("SPLIT MEMORY MODE ENABLED!");
#if SERIAL_DEBUG #if SERIAL_DEBUG
Serial.print("Rows per frame (overall): "); Serial.println(ROWS_PER_FRAME, DEC); Serial.printf("######### Insufficent memory for requested resolution. Reduce MATRIX_COLOR_DEPTH and try again.\r\n\tAdditional %d bytes of memory required.\r\n\r\n", (_frame_buffer_memory_required-heap_caps_get_largest_free_block(MALLOC_CAP_DMA)) );
Serial.print("Rows per split framebuffer malloc: "); Serial.println(SPLIT_MEMORY_ROWS_PER_FRAME, DEC);
#endif #endif
return false;
}
// Can we fit the framebuffer into the single DMA capable memory block available?
if ( heap_caps_get_largest_free_block(MALLOC_CAP_DMA) >= _frame_buffer_memory_required ) { // YES - SIMPLE
// Allocate the framebuffer memory, fail if we can even do this // Allocate the framebuffer 1 memory, fail if we can even do this
matrix_framebuffer_malloc_1 = (frameStruct *)heap_caps_malloc(_frame_buffer_memory_required, MALLOC_CAP_DMA);
if ( matrix_framebuffer_malloc_1 == NULL ) {
#if SERIAL_DEBUG
Serial.println("ERROR: Couldn't malloc matrix_framebuffer_malloc_1! Critical fail.\r\n");
#endif
return false;
}
_total_dma_capable_memory_reserved += _frame_buffer_memory_required;
// SPLIT MEMORY MODE
#ifdef SPLIT_MEMORY_MODE
Serial.println("SPLIT MEMORY MODE ENABLED!");
#if SERIAL_DEBUG
Serial.print("Rows per frame (overall): "); Serial.println(ROWS_PER_FRAME, DEC);
Serial.print("Rows per split framebuffer malloc: "); Serial.println(SPLIT_MEMORY_ROWS_PER_FRAME, DEC);
#endif
// Can we fit the framebuffer into the single DMA capable memory block available?
// Can we fit the framebuffer into the single DMA capable memory block available?
if ( heap_caps_get_largest_free_block(MALLOC_CAP_DMA) < _frame_buffer_memory_required ) {
#if SERIAL_DEBUG
Serial.printf("######### Insufficent memory for second framebuffer for requested resolution. Reduce MATRIX_COLOR_DEPTH and try again.\r\n\tAdditional %d bytes of memory required.\r\n\r\n", (_frame_buffer_memory_required-heap_caps_get_largest_free_block(MALLOC_CAP_DMA)) );
#endif
return false;
}
// Allocate the framebuffer 2 memory, fail if we can even do this
matrix_framebuffer_malloc_2 = (frameStruct *)heap_caps_malloc(_frame_buffer_memory_required, MALLOC_CAP_DMA); matrix_framebuffer_malloc_2 = (frameStruct *)heap_caps_malloc(_frame_buffer_memory_required, MALLOC_CAP_DMA);
if ( !matrix_framebuffer_malloc_2 ) { if ( matrix_framebuffer_malloc_2 == NULL ) {
#if SERIAL_DEBUG #if SERIAL_DEBUG
Serial.println("ERROR: Couldn't malloc matrix_framebuffer_malloc_2! Critical fail.\r\n"); Serial.println("ERROR: Couldn't malloc matrix_framebuffer_malloc_2! Critical fail.\r\n");
#endif #endif
@ -126,7 +142,7 @@ bool RGB64x32MatrixPanel_I2S_DMA::allocateDMAmemory()
} }
_total_dma_capable_memory_reserved += _frame_buffer_memory_required; _total_dma_capable_memory_reserved += _frame_buffer_memory_required;
}
#endif #endif
@ -245,6 +261,15 @@ bool RGB64x32MatrixPanel_I2S_DMA::allocateDMAmemory()
Serial.printf("DMA Memory Available: %d bytes total. Largest free block: %d bytes.\r\n", heap_caps_get_free_size(MALLOC_CAP_DMA), heap_caps_get_largest_free_block(MALLOC_CAP_DMA)); Serial.printf("DMA Memory Available: %d bytes total. Largest free block: %d bytes.\r\n", heap_caps_get_free_size(MALLOC_CAP_DMA), heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
Serial.printf("General RAM Available: %d bytes total. Largest free block: %d bytes.\r\n", heap_caps_get_free_size(MALLOC_CAP_DEFAULT), heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT)); Serial.printf("General RAM Available: %d bytes total. Largest free block: %d bytes.\r\n", heap_caps_get_free_size(MALLOC_CAP_DEFAULT), heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT));
#if SERIAL_DEBUG
Serial.println("DMA memory blocks available after malloc's: ");
heap_caps_print_heap_info(MALLOC_CAP_DMA);
delay(1000);
#endif
return true; return true;
} // end initMatrixDMABuffer() } // end initMatrixDMABuffer()
@ -358,10 +383,12 @@ void RGB64x32MatrixPanel_I2S_DMA::configureDMA(int r1_pin, int g1_pin, int b1_
Serial.println("configureDMA(): DMA configuration completed on I2S1.\r\n"); Serial.println("configureDMA(): DMA configuration completed on I2S1.\r\n");
#endif #endif
#if SERIAL_DEBUG #if SERIAL_DEBUG
Serial.println("DMA Memory Map after allocations: "); Serial.println("DMA Memory Map after DMA LL allocations: ");
heap_caps_print_heap_info(MALLOC_CAP_DMA); heap_caps_print_heap_info(MALLOC_CAP_DMA);
#endif
delay(1000);
#endif
// Just os we know // Just os we know
everything_OK = true; everything_OK = true;
@ -375,11 +402,20 @@ void RGB64x32MatrixPanel_I2S_DMA::configureDMA(int r1_pin, int g1_pin, int b1_
/* Update a specific co-ordinate in the DMA buffer */ /* Update a specific co-ordinate in the DMA buffer */
void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t y_coord, uint8_t red, uint8_t green, uint8_t blue) void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t y_coord, uint8_t red, uint8_t green, uint8_t blue)
{ {
if ( !everything_OK ) {
if ( !everything_OK ) #if SERIAL_DEBUG
assert("DMA configuration in begin() not performed or completed successfully."); Serial.println("Cannot updateMatrixDMABuffer as setup failed!");
#endif
return;
}
#ifdef SPLIT_MEMORY_MODE
#ifdef SERIAL_DEBUG
int tmp_y_coord = y_coord; int tmp_y_coord = y_coord;
#endif
#endif
/* 1) Check that the co-ordinates are within range, or it'll break everything big time. /* 1) Check that the co-ordinates are within range, or it'll break everything big time.
* Valid co-ordinates are from 0 to (MATRIX_XXXX-1) * Valid co-ordinates are from 0 to (MATRIX_XXXX-1)
@ -410,11 +446,11 @@ void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t
#ifdef SPLIT_MEMORY_MODE #ifdef SPLIT_MEMORY_MODE
if (y_coord >= SPLIT_MEMORY_ROWS_PER_FRAME ) { if (y_coord >= SPLIT_MEMORY_ROWS_PER_FRAME ) {
p = &matrix_framebuffer_malloc_2[back_buffer_id].rowdata[(y_coord-SPLIT_MEMORY_ROWS_PER_FRAME)].rowbits[color_depth_idx]; //matrixUpdateFrames location to write to uint16_t's p = &matrix_framebuffer_malloc_2[back_buffer_id].rowdata[(y_coord-SPLIT_MEMORY_ROWS_PER_FRAME)].rowbits[color_depth_idx]; //matrixUpdateFrames location to write to uint16_t's
}
#ifdef SERIAL_DEBUG
// Serial.printf("Using framebuffer_malloc_2. y-coord: %d, matrix row offset: %d \r\n", tmp_y_coord, (y_coord-SPLIT_MEMORY_ROWS_PER_FRAME) );
#endif
#ifdef SERIAL_DEBUG
// Serial.printf("fb_malloc_2. y-coord: %d, calc. offset: %d \r\n", tmp_y_coord, (y_coord-SPLIT_MEMORY_ROWS_PER_FRAME) );
#endif
}
#endif #endif
@ -467,19 +503,18 @@ void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t
if((x_coord) >= lsbBrightness) v|=BIT_OE; // For Brightness if((x_coord) >= lsbBrightness) v|=BIT_OE; // For Brightness
} }
/* When using the drawPixel, we are obviously only changing the value of one x,y position,
* however, the HUB75 is wired up such that it is always painting TWO lines at the same time
* and this reflects the parallel in-DMA-memory data structure of uint16_t's that are getting
/* When using the Adafruit drawPixel, we only have one pixel co-ordinate and colour to draw (duh) * pumped out at high speed.
* so we can't paint a top and bottom half (or whatever row split the panel is) at the same time. *
* Need to be smart and check the DMA buffer to see what the other half thinks (pun intended) * So we need to ensure we persist the bits (8 of them) of the uint16_t for the row we aren't changing.
* and persist this when we refresh.
* *
* The DMA buffer order has also been reversed (refer to the last code in this function) * The DMA buffer order has also been reversed (refer to the last code in this function)
* so we have to check for this and check the correct position of the MATRIX_DATA_STORAGE_TYPE * so we have to check for this and check the correct position of the MATRIX_DATA_STORAGE_TYPE
* data. * data.
*/ */
int16_t tmp_x_coord = x_coord; int tmp_x_coord = x_coord;
if(x_coord%2) if(x_coord%2)
{ {
tmp_x_coord -= 1; tmp_x_coord -= 1;
@ -491,12 +526,9 @@ void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t
{ // Need to copy what the RGB status is for the bottom pixels { // Need to copy what the RGB status is for the bottom pixels
// Set the color of the pixel of interest // Set the color of the pixel of interest
if (green & mask) if (green & mask) { v|=BIT_G1; }
v|=BIT_G1; if (blue & mask) { v|=BIT_B1; }
if (blue & mask) if (red & mask) { v|=BIT_R1; }
v|=BIT_B1;
if (red & mask)
v|=BIT_R1;
// Persist what was painted to the other half of the frame equiv. pixel // Persist what was painted to the other half of the frame equiv. pixel
if (p->data[tmp_x_coord] & BIT_R2) if (p->data[tmp_x_coord] & BIT_R2)
@ -512,12 +544,9 @@ void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t
{ // Do it the other way around { // Do it the other way around
// Color to set // Color to set
if (red & mask) if (red & mask) { v|=BIT_R2; }
v|=BIT_R2; if (green & mask) { v|=BIT_G2; }
if (green & mask) if (blue & mask) { v|=BIT_B2; }
v|=BIT_G2;
if (blue & mask)
v|=BIT_B2;
// Copy // Copy
if (p->data[tmp_x_coord] & BIT_R1) if (p->data[tmp_x_coord] & BIT_R1)
@ -531,10 +560,6 @@ void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t
} // paint } // paint
//Serial.printf("x: %d, y: %d ", x_coord, y_coord );
//Serial.println(v, BIN);
// 16 bit parallel mode // 16 bit parallel mode
//Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering //Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering
if(x_coord%2){ if(x_coord%2){
@ -545,19 +570,14 @@ void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t
} // color depth loop (8) } // color depth loop (8)
//Show our work! } // updateMatrixDMABuffer (specific co-ords change)
//i2s_parallel_flip_to_buffer(&I2S1, backbuf_id);
// If we've linked the DMA output to the same backbuf_id that this function is
// currently writing too, then the output will be immediate. Else: flipDMABuffer(), then showDMABuffer()
} // updateDMABuffer
/* Update the entire buffer with a single specific colour - quicker */ /* Update the entire buffer with a single specific colour - quicker */
void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint8_t red, uint8_t green, uint8_t blue) void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint8_t red, uint8_t green, uint8_t blue)
{ {
if ( !everything_OK ) return;
for (unsigned int matrix_frame_parallel_row = 0; matrix_frame_parallel_row < ROWS_PER_FRAME; matrix_frame_parallel_row++) // half height - 16 iterations for (unsigned int matrix_frame_parallel_row = 0; matrix_frame_parallel_row < ROWS_PER_FRAME; matrix_frame_parallel_row++) // half height - 16 iterations
{ {
for(int color_depth_idx=0; color_depth_idx<PIXEL_COLOR_DEPTH_BITS; color_depth_idx++) // color depth - 8 iterations for(int color_depth_idx=0; color_depth_idx<PIXEL_COLOR_DEPTH_BITS; color_depth_idx++) // color depth - 8 iterations
@ -572,101 +592,86 @@ void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint8_t red, uint8_t gre
p = &matrix_framebuffer_malloc_2[back_buffer_id].rowdata[(matrix_frame_parallel_row-SPLIT_MEMORY_ROWS_PER_FRAME)].rowbits[color_depth_idx]; //matrixUpdateFrames location to write to uint16_t's p = &matrix_framebuffer_malloc_2[back_buffer_id].rowdata[(matrix_frame_parallel_row-SPLIT_MEMORY_ROWS_PER_FRAME)].rowbits[color_depth_idx]; //matrixUpdateFrames location to write to uint16_t's
#ifdef SERIAL_DEBUG #ifdef SERIAL_DEBUG
// Serial.printf("Using framebuffer_malloc_2. Row %d = Offset %d\r\n", matrix_frame_parallel_row, (matrix_frame_parallel_row-SPLIT_MEMORY_ROWS_PER_FRAME) ); //Serial.printf("Using framebuffer_malloc_2. Row %d = Offset %d\r\n", matrix_frame_parallel_row, (matrix_frame_parallel_row-SPLIT_MEMORY_ROWS_PER_FRAME) );
#endif
}
else
{
#ifdef SERIAL_DEBUG
// Serial.printf("Using framebuffer_malloc_1. Row %d\r\n", matrix_frame_parallel_row );
#endif #endif
} }
#endif #endif
for(int x_coord=0; x_coord < MATRIX_WIDTH; x_coord++) // row pixel width 64 iterations for(int x_coord=0; x_coord < MATRIX_WIDTH; x_coord++) // row pixel width 64 iterations
{ {
int v=0; // the output bitstream int v=0; // the output bitstream
// if there is no latch to hold address, output ADDX lines directly to GPIO and latch data at end of cycle // if there is no latch to hold address, output ADDX lines directly to GPIO and latch data at end of cycle
int gpioRowAddress = matrix_frame_parallel_row; int gpioRowAddress = matrix_frame_parallel_row;
// normally output current rows ADDX, special case for LSB, output previous row's ADDX (as previous row is being displayed for one latch cycle) // normally output current rows ADDX, special case for LSB, output previous row's ADDX (as previous row is being displayed for one latch cycle)
if(color_depth_idx == 0) if(color_depth_idx == 0)
gpioRowAddress = matrix_frame_parallel_row-1; gpioRowAddress = matrix_frame_parallel_row-1;
if (gpioRowAddress & 0x01) v|=BIT_A; // 1 if (gpioRowAddress & 0x01) v|=BIT_A; // 1
if (gpioRowAddress & 0x02) v|=BIT_B; // 2 if (gpioRowAddress & 0x02) v|=BIT_B; // 2
if (gpioRowAddress & 0x04) v|=BIT_C; // 4 if (gpioRowAddress & 0x04) v|=BIT_C; // 4
if (gpioRowAddress & 0x08) v|=BIT_D; // 8 if (gpioRowAddress & 0x08) v|=BIT_D; // 8
if (gpioRowAddress & 0x10) v|=BIT_E; // 16 if (gpioRowAddress & 0x10) v|=BIT_E; // 16
/* ORIG /* ORIG
// need to disable OE after latch to hide row transition // need to disable OE after latch to hide row transition
if((x_coord) == 0) v|=BIT_OE; if((x_coord) == 0) v|=BIT_OE;
// drive latch while shifting out last bit of RGB data // drive latch while shifting out last bit of RGB data
if((x_coord) == PIXELS_PER_LATCH-1) v|=BIT_LAT; if((x_coord) == PIXELS_PER_LATCH-1) v|=BIT_LAT;
// need to turn off OE one clock before latch, otherwise can get ghosting // need to turn off OE one clock before latch, otherwise can get ghosting
if((x_coord)==PIXELS_PER_LATCH-1) v|=BIT_OE; if((x_coord)==PIXELS_PER_LATCH-1) v|=BIT_OE;
*/ */
// need to disable OE after latch to hide row transition // need to disable OE after latch to hide row transition
if((x_coord) == 0 ) v|=BIT_OE; if((x_coord) == 0 ) v|=BIT_OE;
// drive latch while shifting out last bit of RGB data // drive latch while shifting out last bit of RGB data
if((x_coord) == PIXELS_PER_ROW-1) v|=BIT_LAT; if((x_coord) == PIXELS_PER_ROW-1) v|=BIT_LAT;
// need to turn off OE one clock before latch, otherwise can get ghosting // need to turn off OE one clock before latch, otherwise can get ghosting
if((x_coord)==PIXELS_PER_ROW-2) v|=BIT_OE; if((x_coord)==PIXELS_PER_ROW-2) v|=BIT_OE;
// turn off OE after brightness value is reached when displaying MSBs // turn off OE after brightness value is reached when displaying MSBs
// MSBs always output normal brightness // MSBs always output normal brightness
// LSB (!color_depth_idx) outputs normal brightness as MSB from previous row is being displayed // LSB (!color_depth_idx) outputs normal brightness as MSB from previous row is being displayed
if((color_depth_idx > lsbMsbTransitionBit || !color_depth_idx) && ((x_coord) >= brightness)) v|=BIT_OE; // For Brightness if((color_depth_idx > lsbMsbTransitionBit || !color_depth_idx) && ((x_coord) >= brightness)) v|=BIT_OE; // For Brightness
// special case for the bits *after* LSB through (lsbMsbTransitionBit) - OE is output after data is shifted, so need to set OE to fractional brightness // special case for the bits *after* LSB through (lsbMsbTransitionBit) - OE is output after data is shifted, so need to set OE to fractional brightness
if(color_depth_idx && color_depth_idx <= lsbMsbTransitionBit) { if(color_depth_idx && color_depth_idx <= lsbMsbTransitionBit) {
// divide brightness in half for each bit below lsbMsbTransitionBit // divide brightness in half for each bit below lsbMsbTransitionBit
int lsbBrightness = brightness >> (lsbMsbTransitionBit - color_depth_idx + 1); int lsbBrightness = brightness >> (lsbMsbTransitionBit - color_depth_idx + 1);
if((x_coord) >= lsbBrightness) v|=BIT_OE; // For Brightness if((x_coord) >= lsbBrightness) v|=BIT_OE; // For Brightness
} }
// Top and bottom matrix MATRIX_ROWS_IN_PARALLEL half colours
if (green & mask) { v|=BIT_G1; v|=BIT_R2; }
if (blue & mask) { v|=BIT_B1; v|=BIT_G2; }
if (red & mask) { v|=BIT_R1; v|=BIT_B2; }
// Top half colours // 16 bit parallel mode
if (green & mask) //Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering
v|=BIT_G1; if(x_coord%2) {
if (blue & mask) p->data[(x_coord)-1] = v;
v|=BIT_B1; } else {
if (red & mask) p->data[(x_coord)+1] = v;
v|=BIT_R1; } // end reordering
// Bottom half colours } // end x_coord iteration
if (red & mask)
v|=BIT_R2;
if (green & mask)
v|=BIT_G2;
if (blue & mask)
v|=BIT_B2;
// 16 bit parallel mode
//Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering
if(x_coord%2)
{
p->data[(x_coord)-1] = v;
} else {
p->data[(x_coord)+1] = v;
} // end reordering
} // end x_coord iteration
} // colour depth loop (8) } // colour depth loop (8)
} // end row iteration } // end row iteration
} // updateMatrixDMABuffer (full frame paint)
//Show our work!
//i2s_parallel_flip_to_buffer(&I2S1, backbuf_id);
// If we've linked the DMA output to the same backbuf_id that this function is
// currently writing too, then the output will be immediate. Else: flipDMABuffer(), then showDMABuffer()
} // updateDMABuffer

View file

@ -35,7 +35,7 @@
* *
*/ */
#ifndef MATRIX_HEIGHT #ifndef MATRIX_HEIGHT
#define MATRIX_HEIGHT 32 //64 #define MATRIX_HEIGHT 32
#endif #endif
#ifndef MATRIX_WIDTH #ifndef MATRIX_WIDTH
@ -210,6 +210,11 @@ class RGB64x32MatrixPanel_I2S_DMA : public GFX {
showDMABuffer(); // show backbuf_id of 0 showDMABuffer(); // show backbuf_id of 0
#if SERIAL_DEBUG
if (!everything_OK)
Serial.println("RGB64x32MatrixPanel_I2S_DMA::begin() failed.");
#endif
return everything_OK; return everything_OK;
} }

View file

@ -64,7 +64,7 @@ A [typical RGB panel available for purchase](https://www.aliexpress.com/item/256
## Can I chain panels or use with larger panels? ## Can I chain panels or use with larger panels?
Yes you can. If you want to use with a 64x64 pixel panel you MUST configure a valid *E_PIN* to your ESP32 and connect it to the E pin of the HUB75 panel! Yes you can. If you want to use with a 64x64 pixel panel (typically a HUB75*E* panel) you MUST configure a valid *E_PIN* to your ESP32 and connect it to the E pin of the HUB75 panel! Hence the 'E' in 'HUB75E'
This library has only been tested with a 64 pixel (wide) and 32 (high) RGB panel. Theoretically, if you want to chain two of these horizontally to make a 128x32 panel you can do so with the cable and then set the MATRIX_WIDTH to '128'. This library has only been tested with a 64 pixel (wide) and 32 (high) RGB panel. Theoretically, if you want to chain two of these horizontally to make a 128x32 panel you can do so with the cable and then set the MATRIX_WIDTH to '128'.

View file

@ -29,9 +29,11 @@
#include "driver/periph_ctrl.h" #include "driver/periph_ctrl.h"
#include "soc/io_mux_reg.h" #include "soc/io_mux_reg.h"
#include "rom/lldesc.h" #include "rom/lldesc.h"
#include "esp_heap_caps.h" //#include "esp_heap_caps.h"
#include "esp32_i2s_parallel.h" #include "esp32_i2s_parallel.h"
#define DMA_MAX (4096-4)
typedef struct { typedef struct {
volatile lldesc_t *dmadesc_a, *dmadesc_b; volatile lldesc_t *dmadesc_a, *dmadesc_b;
int desccount_a, desccount_b; int desccount_a, desccount_b;
@ -61,9 +63,7 @@ static void IRAM_ATTR i2s_isr(void* arg) {
if(shiftCompleteCallback) if(shiftCompleteCallback)
shiftCompleteCallback(); shiftCompleteCallback();
} }
/*
#define DMA_MAX (4096-4)
//Calculate the amount of dma descs needed for a buffer desc //Calculate the amount of dma descs needed for a buffer desc
static int calc_needed_dma_descs_for(i2s_parallel_buffer_desc_t *desc) { static int calc_needed_dma_descs_for(i2s_parallel_buffer_desc_t *desc) {
int ret=0; int ret=0;
@ -102,8 +102,10 @@ static void fill_dma_desc(volatile lldesc_t *dmadesc, i2s_parallel_buffer_desc_t
printf("fill_dma_desc: filled %d descriptors\n", n); printf("fill_dma_desc: filled %d descriptors\n", n);
} }
*/
// size must be less than DMA_MAX - need to handle breaking long transfer into two descriptors before call // 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; if(size > DMA_MAX) size = DMA_MAX;
@ -128,7 +130,6 @@ static void gpio_setup_out(int gpio, int sig) {
gpio_matrix_out(gpio, sig, false, false); gpio_matrix_out(gpio, sig, false, false);
} }
static void dma_reset(i2s_dev_t *dev) { static void dma_reset(i2s_dev_t *dev) {
dev->lc_conf.in_rst=1; dev->lc_conf.in_rst=0; dev->lc_conf.in_rst=1; dev->lc_conf.in_rst=0;
dev->lc_conf.out_rst=1; dev->lc_conf.out_rst=0; dev->lc_conf.out_rst=1; dev->lc_conf.out_rst=0;
@ -147,14 +148,14 @@ void i2s_parallel_setup_without_malloc(i2s_dev_t *dev, const i2s_parallel_config
sig_data_base=I2S0O_DATA_OUT0_IDX; sig_data_base=I2S0O_DATA_OUT0_IDX;
sig_clk=I2S0O_WS_OUT_IDX; sig_clk=I2S0O_WS_OUT_IDX;
} else { } else {
printf("Setting up i2s parallel mode in %d bit mode!\n", cfg->bits);
if (cfg->bits==I2S_PARALLEL_BITS_32) { if (cfg->bits==I2S_PARALLEL_BITS_32) {
sig_data_base=I2S1O_DATA_OUT0_IDX; sig_data_base=I2S1O_DATA_OUT0_IDX;
} else if (cfg->bits==I2S_PARALLEL_BITS_16) { } else if (cfg->bits==I2S_PARALLEL_BITS_16) {
//Because of... reasons... the 16-bit values for i2s1 appear on d8...d23 //Because of... reasons... the 16-bit values for i2s1 appear on d8...d23
printf("Setting up i2s parallel mode in 16 bit mode!");
sig_data_base=I2S1O_DATA_OUT8_IDX; sig_data_base=I2S1O_DATA_OUT8_IDX;
} else { // I2S_PARALLEL_BITS_8 } else { // I2S_PARALLEL_BITS_8
printf("Setting up i2s parallel mode in 8 bit mode -> https://www.esp32.com/viewtopic.php?f=17&t=3188 | https://www.esp32.com/viewtopic.php?f=13&t=3256"); //printf("Setting up i2s parallel mode in %d bit mode -> https://www.esp32.com/viewtopic.php?f=17&t=3188 | https://www.esp32.com/viewtopic.php?f=13&t=3256", 8);
sig_data_base=I2S1O_DATA_OUT0_IDX; sig_data_base=I2S1O_DATA_OUT0_IDX;
} }
sig_clk=I2S1O_WS_OUT_IDX; sig_clk=I2S1O_WS_OUT_IDX;
@ -251,7 +252,7 @@ void i2s_parallel_setup_without_malloc(i2s_dev_t *dev, const i2s_parallel_config
// allocate a level 1 intterupt: lowest priority, as ISR isn't urgent and may take a long time to complete // allocate a level 1 intterupt: lowest priority, as ISR isn't urgent and may take a long time to complete
esp_intr_alloc(ETS_I2S1_INTR_SOURCE, (int)(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1), i2s_isr, NULL, NULL); esp_intr_alloc(ETS_I2S1_INTR_SOURCE, (int)(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1), i2s_isr, NULL, NULL);
//Start dma on front buffer //Start dma on front buffer (buffer a)
dev->lc_conf.val=I2S_OUT_DATA_BURST_EN | I2S_OUTDSCR_BURST_EN | I2S_OUT_DATA_BURST_EN; dev->lc_conf.val=I2S_OUT_DATA_BURST_EN | I2S_OUTDSCR_BURST_EN | I2S_OUT_DATA_BURST_EN;
dev->out_link.addr=((uint32_t)(&st->dmadesc_a[0])); dev->out_link.addr=((uint32_t)(&st->dmadesc_a[0]));
dev->out_link.start=1; dev->out_link.start=1;
@ -281,4 +282,5 @@ bool i2s_parallel_is_previous_buffer_free() {
return previousBufferFree; return previousBufferFree;
} }
#endif #endif

View file

@ -13,7 +13,7 @@ extern "C" {
#include "rom/lldesc.h" #include "rom/lldesc.h"
typedef enum { typedef enum {
I2S_PARALLEL_BITS_8=8, // BUG: Doesn't work. I2S_PARALLEL_BITS_8=8,
I2S_PARALLEL_BITS_16=16, I2S_PARALLEL_BITS_16=16,
I2S_PARALLEL_BITS_32=32, I2S_PARALLEL_BITS_32=32,
} i2s_parallel_cfg_bits_t; } i2s_parallel_cfg_bits_t;
@ -29,18 +29,20 @@ typedef struct {
int clkspeed_hz; int clkspeed_hz;
i2s_parallel_cfg_bits_t bits; i2s_parallel_cfg_bits_t bits;
i2s_parallel_buffer_desc_t *bufa; i2s_parallel_buffer_desc_t *bufa;
i2s_parallel_buffer_desc_t *bufb; i2s_parallel_buffer_desc_t *bufb; // only used with double buffering
int desccount_a; int desccount_a;
int desccount_b; int desccount_b; // only used with double buffering
lldesc_t * lldesc_a; lldesc_t * lldesc_a;
lldesc_t * lldesc_b; lldesc_t * lldesc_b; // only used with double buffering
} i2s_parallel_config_t; } i2s_parallel_config_t;
void i2s_parallel_setup(i2s_dev_t *dev, const i2s_parallel_config_t *cfg);
void i2s_parallel_setup_without_malloc(i2s_dev_t *dev, const i2s_parallel_config_t *cfg); void i2s_parallel_setup_without_malloc(i2s_dev_t *dev, const i2s_parallel_config_t *cfg);
void link_dma_desc(volatile lldesc_t *dmadesc, volatile lldesc_t *prevdmadesc, void *memory, size_t size);
void i2s_parallel_flip_to_buffer(i2s_dev_t *dev, int bufid); void i2s_parallel_flip_to_buffer(i2s_dev_t *dev, int bufid);
bool i2s_parallel_is_previous_buffer_free(); bool i2s_parallel_is_previous_buffer_free();
void link_dma_desc(volatile lldesc_t *dmadesc, volatile lldesc_t *prevdmadesc, void *memory, size_t size);
typedef void (*callback)(void); typedef void (*callback)(void);
void setShiftCompleteCallback(callback f); void setShiftCompleteCallback(callback f);