Added double buffering / back buffer example
Current implementation simply write to the active DMA buffer, but you might want to use both buffers and do off-screen writes.
This commit is contained in:
parent
c66fced326
commit
5f8388eac9
3 changed files with 166 additions and 55 deletions
|
@ -199,7 +199,7 @@ void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// What half of the HUB75 panel are we painting too?
|
// What half of the HUB75 panel are we painting to?
|
||||||
bool paint_top_half = true;
|
bool paint_top_half = true;
|
||||||
if ( y_coord > ROWS_PER_FRAME-1) // co-ords start at zero, y_coord = 15 = 16 (rows per frame)
|
if ( y_coord > ROWS_PER_FRAME-1) // co-ords start at zero, y_coord = 15 = 16 (rows per frame)
|
||||||
{
|
{
|
||||||
|
@ -257,7 +257,7 @@ void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t
|
||||||
* Need to be smart and check the DMA buffer to see what the other half thinks (pun intended)
|
* Need to be smart and check the DMA buffer to see what the other half thinks (pun intended)
|
||||||
* and persist this when we refresh.
|
* and persist this when we refresh.
|
||||||
*
|
*
|
||||||
* The DMA buffer order has also been reversed (fer 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.
|
||||||
*/
|
*/
|
||||||
|
@ -324,19 +324,12 @@ void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t
|
||||||
|
|
||||||
} // color depth loop (8)
|
} // color depth loop (8)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Show our work!
|
//Show our work!
|
||||||
if (immediateUpdate)
|
//i2s_parallel_flip_to_buffer(&I2S1, backbuf_id);
|
||||||
refreshDMAOutput();
|
|
||||||
//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()
|
||||||
|
|
||||||
// There's no reason you'd want to do this in the draw pixel routine.
|
|
||||||
if (autoBackBufferFlip)
|
|
||||||
swapBuffer();
|
|
||||||
*/
|
|
||||||
|
|
||||||
} // updateDMABuffer
|
} // updateDMABuffer
|
||||||
|
|
||||||
|
@ -425,22 +418,12 @@ void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint8_t red, uint8_t gre
|
||||||
} // end row iteration
|
} // end row iteration
|
||||||
|
|
||||||
|
|
||||||
if (immediateUpdate)
|
|
||||||
refreshDMAOutput();
|
|
||||||
//i2s_parallel_flip_to_buffer(&I2S1, backbuf_id);
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
// There's no reason you'd want to do this here either to be honest
|
|
||||||
if (autoBackBufferFlip)
|
|
||||||
swapBuffer();
|
|
||||||
*/
|
|
||||||
|
|
||||||
//Show our work!
|
//Show our work!
|
||||||
//i2s_parallel_flip_to_buffer(&I2S1, backbuf_id);
|
//i2s_parallel_flip_to_buffer(&I2S1, backbuf_id);
|
||||||
|
|
||||||
|
// If we've linked the DMA output to the same backbuf_id that this function is
|
||||||
//swapBuffer();
|
// currently writing too, then the output will be immediate. Else: flipDMABuffer(), then showDMABuffer()
|
||||||
|
|
||||||
} // updateDMABuffer
|
} // updateDMABuffer
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#include "Adafruit_GFX.h"
|
#include "Adafruit_GFX.h"
|
||||||
|
|
||||||
|
#define SERIAL_DEBUG_OUTPUT 1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
This is example code to driver a p3(2121)64*32 -style RGB LED display. These types of displays do not have memory and need to be refreshed
|
This is example code to driver a p3(2121)64*32 -style RGB LED display. These types of displays do not have memory and need to be refreshed
|
||||||
|
@ -88,10 +90,10 @@
|
||||||
#define B2_PIN_DEFAULT 13
|
#define B2_PIN_DEFAULT 13
|
||||||
|
|
||||||
#define A_PIN_DEFAULT 23
|
#define A_PIN_DEFAULT 23
|
||||||
#define B_PIN_DEFAULT 22
|
#define B_PIN_DEFAULT 19
|
||||||
#define C_PIN_DEFAULT 5
|
#define C_PIN_DEFAULT 5
|
||||||
#define D_PIN_DEFAULT 17
|
#define D_PIN_DEFAULT 17
|
||||||
#define E_PIN_DEFAULT -1
|
#define E_PIN_DEFAULT -1 // Change to a valid pin if using a 64 pixel row panel.
|
||||||
|
|
||||||
#define LAT_PIN_DEFAULT 4
|
#define LAT_PIN_DEFAULT 4
|
||||||
#define OE_PIN_DEFAULT 15
|
#define OE_PIN_DEFAULT 15
|
||||||
|
@ -175,11 +177,11 @@ typedef struct rgb_24 {
|
||||||
class RGB64x32MatrixPanel_I2S_DMA : public Adafruit_GFX {
|
class RGB64x32MatrixPanel_I2S_DMA : public Adafruit_GFX {
|
||||||
// ------- PUBLIC -------
|
// ------- PUBLIC -------
|
||||||
public:
|
public:
|
||||||
RGB64x32MatrixPanel_I2S_DMA(bool _immediateUpdate = true, bool _autoBackBufferFlip = false) // Refer to commentary in the private: section
|
RGB64x32MatrixPanel_I2S_DMA(bool _doubleBuffer = false) // Double buffer is disabled by default. Any change will display next active DMA buffer output (very quickly). NOTE: Not Implemented
|
||||||
: Adafruit_GFX(MATRIX_WIDTH, MATRIX_HEIGHT), immediateUpdate(_immediateUpdate), autoBackBufferFlip(_autoBackBufferFlip) {
|
: Adafruit_GFX(MATRIX_WIDTH, MATRIX_HEIGHT), doubleBuffer(_doubleBuffer) {
|
||||||
|
|
||||||
backbuf_id = 0;
|
backbuf_id = 0;
|
||||||
brightness = 32; // default to max brightness, wear sunglasses when looking directly at panel.
|
brightness = 64; // default to max brightness, wear sunglasses when looking directly at panel.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,10 +192,10 @@ class RGB64x32MatrixPanel_I2S_DMA : public Adafruit_GFX {
|
||||||
/* As DMA buffers are dynamically allocated, we must allocated in begin()
|
/* As DMA buffers are dynamically allocated, we must allocated in begin()
|
||||||
* Ref: https://github.com/espressif/arduino-esp32/issues/831
|
* Ref: https://github.com/espressif/arduino-esp32/issues/831
|
||||||
*/
|
*/
|
||||||
allocateDMAbuffers();
|
allocateDMAbuffers();
|
||||||
|
|
||||||
// Change 'if' to '1' to enable, 0 to not include this Serial output in compiled program
|
// Change 'if' to '1' to enable, 0 to not include this Serial output in compiled program
|
||||||
#if 1
|
#if SERIAL_DEBUG_OUTPUT
|
||||||
Serial.printf("Using pin %d for the R1_PIN\n", dma_r1_pin);
|
Serial.printf("Using pin %d for the R1_PIN\n", dma_r1_pin);
|
||||||
Serial.printf("Using pin %d for the G1_PIN\n", dma_g1_pin);
|
Serial.printf("Using pin %d for the G1_PIN\n", dma_g1_pin);
|
||||||
Serial.printf("Using pin %d for the B1_PIN\n", dma_b1_pin);
|
Serial.printf("Using pin %d for the B1_PIN\n", dma_b1_pin);
|
||||||
|
@ -211,12 +213,18 @@ class RGB64x32MatrixPanel_I2S_DMA : public Adafruit_GFX {
|
||||||
Serial.printf("Using pin %d for the CLK_PIN\n", dma_clk_pin);
|
Serial.printf("Using pin %d for the CLK_PIN\n", dma_clk_pin);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Flush the DMA buffers prior to configuring DMA - Avoid visual artefacts on boot.
|
||||||
|
flushDMAbuffer();
|
||||||
|
flipDMABuffer(); // flip to backbuffer 1
|
||||||
|
flushDMAbuffer();
|
||||||
|
flipDMABuffer(); // backbuffer 0
|
||||||
|
|
||||||
|
// Setup the ESP32 DMA Engine. Sprite_TM built this stuff.
|
||||||
configureDMA(dma_r1_pin, dma_g1_pin, dma_b1_pin, dma_r2_pin, dma_g2_pin, dma_b2_pin, dma_a_pin, dma_b_pin, dma_c_pin, dma_d_pin, dma_e_pin, dma_lat_pin, dma_oe_pin, dma_clk_pin ); //DMA and I2S configuration and setup
|
configureDMA(dma_r1_pin, dma_g1_pin, dma_b1_pin, dma_r2_pin, dma_g2_pin, dma_b2_pin, dma_a_pin, dma_b_pin, dma_c_pin, dma_d_pin, dma_e_pin, dma_lat_pin, dma_oe_pin, dma_clk_pin ); //DMA and I2S configuration and setup
|
||||||
|
|
||||||
flushDMAbuffer();
|
showDMABuffer(); // show 0
|
||||||
swapBuffer();
|
|
||||||
flushDMAbuffer();
|
|
||||||
swapBuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Disable/Enable auto buffer flipping (useful for lots of drawPixel usage)...
|
// TODO: Disable/Enable auto buffer flipping (useful for lots of drawPixel usage)...
|
||||||
|
@ -241,11 +249,23 @@ class RGB64x32MatrixPanel_I2S_DMA : public Adafruit_GFX {
|
||||||
// Converts RGB888 to RGB565
|
// Converts RGB888 to RGB565
|
||||||
uint16_t color565(uint8_t r, uint8_t g, uint8_t b); // This is what is used by Adafruit GFX!
|
uint16_t color565(uint8_t r, uint8_t g, uint8_t b); // This is what is used by Adafruit GFX!
|
||||||
|
|
||||||
void swapBuffer() {
|
void flipDMABuffer()
|
||||||
backbuf_id ^=1;
|
{
|
||||||
|
// Step 1. Bring backbuffer to the foreground (i.e. show it)
|
||||||
|
//showDMABuffer();
|
||||||
|
|
||||||
|
// Step 2. Copy foreground to backbuffer
|
||||||
|
//matrixUpdateFrames[backbuf_id ^ 1] = matrixUpdateFrames[backbuf_id]; // copy currently being displayed buffer to backbuffer
|
||||||
|
|
||||||
|
// Step 3. Change to this new buffer as the backbuffer
|
||||||
|
backbuf_id ^=1; // set this now to the back_buffer to update (not displayed yet though)
|
||||||
|
|
||||||
|
#if SERIAL_DEBUG_OUTPUT
|
||||||
|
Serial.printf("Set back buffer to: %d\n", backbuf_id);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void refreshDMAOutput()
|
void showDMABuffer()
|
||||||
{
|
{
|
||||||
i2s_parallel_flip_to_buffer(&I2S1, backbuf_id);
|
i2s_parallel_flip_to_buffer(&I2S1, backbuf_id);
|
||||||
}
|
}
|
||||||
|
@ -269,7 +289,7 @@ class RGB64x32MatrixPanel_I2S_DMA : public Adafruit_GFX {
|
||||||
|
|
||||||
void flushDMAbuffer()
|
void flushDMAbuffer()
|
||||||
{
|
{
|
||||||
Serial.printf("Flushing buffer %d", backbuf_id);
|
Serial.printf("Flushing buffer %d\n", backbuf_id);
|
||||||
// Need to wipe the contents of the matrix buffers or weird things happen.
|
// Need to wipe the contents of the matrix buffers or weird things happen.
|
||||||
for (int y=0;y<MATRIX_HEIGHT; y++)
|
for (int y=0;y<MATRIX_HEIGHT; y++)
|
||||||
for (int x=0;x<MATRIX_WIDTH; x++)
|
for (int x=0;x<MATRIX_WIDTH; x++)
|
||||||
|
@ -288,20 +308,18 @@ class RGB64x32MatrixPanel_I2S_DMA : public Adafruit_GFX {
|
||||||
// Update the entire DMA buffer (aka. The RGB Panel) a certain colour (wipe the screen basically)
|
// Update the entire DMA buffer (aka. The RGB Panel) a certain colour (wipe the screen basically)
|
||||||
void updateMatrixDMABuffer(uint8_t red, uint8_t green, uint8_t blue);
|
void updateMatrixDMABuffer(uint8_t red, uint8_t green, uint8_t blue);
|
||||||
|
|
||||||
// Internal variables
|
// Pixel data is organized from LSB to MSB sequentially by row, from row 0 to row matrixHeight/matrixRowsInParallel (two rows of pixels are refreshed in parallel)
|
||||||
bool immediateUpdate; // Purpose: as per the variable name says, the minute we change a pixel, tell the ESP32 to use this for the I2S DMA Output
|
frameStruct *matrixUpdateFrames;
|
||||||
bool autoBackBufferFlip; // (Note: currently not used) Purpose: do we use the other buffer automatically after an update, or change to the 2nd buffer manually? Only use this if you know what you're doing. Otherwise for example, calling drawPixel three times (i.e. you're updating 3 pixels), will update pixel 1 on buffer 0, pixel on buffer 1, and pixel 3 on buffer 0 again - so you'll get weird output.
|
|
||||||
|
|
||||||
// Setup
|
// Setup
|
||||||
bool dma_configuration_success;
|
bool dma_configuration_success;
|
||||||
|
|
||||||
// Pixel data is organized from LSB to MSB sequentially by row, from row 0 to row matrixHeight/matrixRowsInParallel (two rows of pixels are refreshed in parallel)
|
// Internal variables
|
||||||
frameStruct *matrixUpdateFrames;
|
bool doubleBuffer; // Do we use double buffer mode? Your project code will have to manually flip between both.
|
||||||
|
int backbuf_id; // If using double buffer, which one is NOT active (ie. being displayed) to write too?
|
||||||
|
|
||||||
int lsbMsbTransitionBit;
|
int lsbMsbTransitionBit;
|
||||||
int refreshRate;
|
int refreshRate;
|
||||||
|
|
||||||
int backbuf_id; // which buffer is the DMA backbuffer, as in, which one is not active so we can write to it
|
|
||||||
int brightness;
|
int brightness;
|
||||||
|
|
||||||
}; // end Class header
|
}; // end Class header
|
||||||
|
|
110
examples/Buffer_Swap_Test/Buffer_Swap_Test.ino
Normal file
110
examples/Buffer_Swap_Test/Buffer_Swap_Test.ino
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
#include <ESP32-RGB64x32MatrixPanel-I2S-DMA.h>
|
||||||
|
|
||||||
|
RGB64x32MatrixPanel_I2S_DMA display; // RGB Panel
|
||||||
|
|
||||||
|
const byte row0 = 2+0*10;
|
||||||
|
const byte row1 = 2+1*10;
|
||||||
|
const byte row2 = 2+2*10;
|
||||||
|
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
|
||||||
|
// put your setup code here, to run once:
|
||||||
|
delay(1000); Serial.begin(115200); delay(200);
|
||||||
|
|
||||||
|
Serial.println("...Starting Display");
|
||||||
|
display.begin(); // setup the display with no double buffering
|
||||||
|
display.setTextColor(display.color565(128, 128, 128));
|
||||||
|
|
||||||
|
|
||||||
|
// Buffer 0 test
|
||||||
|
display.fillScreen(display.color565(128, 0, 0));
|
||||||
|
display.setCursor(3, row0);
|
||||||
|
display.print(F("Buffer 0"));
|
||||||
|
display.setCursor(3, row1);
|
||||||
|
display.print(F(" Buffer 0"));
|
||||||
|
Serial.println("Wrote to to Buffer 0");
|
||||||
|
display.showDMABuffer();
|
||||||
|
delay(1500);
|
||||||
|
|
||||||
|
// Buffer 1 test
|
||||||
|
display.flipDMABuffer();
|
||||||
|
display.fillScreen(display.color565(0, 128, 0)); // shouldn't see this
|
||||||
|
display.setCursor(3, row0);
|
||||||
|
display.print(F("Buffer 1"));
|
||||||
|
display.setCursor(3, row2);
|
||||||
|
display.print(F(" Buffer 1"));
|
||||||
|
|
||||||
|
Serial.println("Wrote to to Buffer 1");
|
||||||
|
display.showDMABuffer();
|
||||||
|
delay(1500);
|
||||||
|
/*
|
||||||
|
delay(500);
|
||||||
|
display.fillScreen(display.color565(0, 0, 128));
|
||||||
|
delay(1000);
|
||||||
|
display.flipDMABuffer();
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
display.flipDMABuffer();
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
// Flip the back buffer
|
||||||
|
display.flipDMABuffer();
|
||||||
|
|
||||||
|
// Write: Set bottow row to black
|
||||||
|
for (int y=20;y<MATRIX_HEIGHT; y++)
|
||||||
|
for (int x=0;x<MATRIX_WIDTH; x++)
|
||||||
|
{
|
||||||
|
display.drawPixelRGB888( x, y, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write: Set bottom row to blue (this is what should show)
|
||||||
|
for (int y=20;y<MATRIX_HEIGHT; y++)
|
||||||
|
for (int x=0;x<MATRIX_WIDTH; x++)
|
||||||
|
{
|
||||||
|
display.drawPixelRGB888( x, y, 0, 0, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now show this back buffer
|
||||||
|
display.showDMABuffer();
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
// Flip back buffer
|
||||||
|
display.flipDMABuffer();
|
||||||
|
|
||||||
|
// Show this buffer
|
||||||
|
display.showDMABuffer();
|
||||||
|
delay(1000);
|
||||||
|
/*
|
||||||
|
//Parola.flushBufferAll
|
||||||
|
|
||||||
|
// UpdateBackground
|
||||||
|
for (int y=20;y<MATRIX_HEIGHT; y++)
|
||||||
|
for (int x=0;x<MATRIX_WIDTH; x++)
|
||||||
|
{
|
||||||
|
//Serial.printf("\r\nFlushing x, y coord %d, %d", x, y);
|
||||||
|
display.drawPixelRGB888( x, y, 0, 128, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int y=20;y<MATRIX_HEIGHT; y++)
|
||||||
|
for (int x=0;x<MATRIX_WIDTH; x++)
|
||||||
|
{
|
||||||
|
//Serial.printf("\r\nFlushing x, y coord %d, %d", x, y);
|
||||||
|
display.drawPixelRGB888( x, y, 64, 0, 0);
|
||||||
|
}
|
||||||
|
// display.refreshDMAOutput();
|
||||||
|
|
||||||
|
//display.flipDMABuffer();
|
||||||
|
delay(20);
|
||||||
|
display.showDMABuffer();
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue