From 4be4f12c89736ca8298e5e43b2313c8b3d307380 Mon Sep 17 00:00:00 2001 From: Rasmus Friis Kjeldsen Date: Sat, 1 Jun 2019 11:38:35 +0200 Subject: [PATCH] Double buffer: Intensity flicker fix, all Adafruit_GFX draw functions now works w double buffer, simplified double buffer usage, added double_buffer example sketch . clearDisplay() a lot faster --- PxMatrix.h | 157 +++++++++++------------ examples/double_buffer/double_buffer.ino | 115 +++++++++++++++++ 2 files changed, 189 insertions(+), 83 deletions(-) create mode 100644 examples/double_buffer/double_buffer.ino diff --git a/PxMatrix.h b/PxMatrix.h index 2e7c0c8..09fda0c 100644 --- a/PxMatrix.h +++ b/PxMatrix.h @@ -84,13 +84,13 @@ enum scan_patterns {LINE, ZIGZAG, ZAGGIZ, WZAGZIG, VZAG}; enum driver_chips {SHIFT, FM6124, FM6126A}; -#define max_matrix_pixels PxMATRIX_MAX_HEIGHT * PxMATRIX_MAX_WIDTH -#define color_step 256 / PxMATRIX_COLOR_DEPTH -#define color_half_step int(color_step / 2) -#define color_third_step int(color_step / 3) -#define color_two_third_step int(color_third_step*2) +#define max_matrix_pixels (PxMATRIX_MAX_HEIGHT * PxMATRIX_MAX_WIDTH) +#define color_step (256 / PxMATRIX_COLOR_DEPTH) +#define color_half_step (int(color_step / 2)) +#define color_third_step (int(color_step / 3)) +#define color_two_third_step (int(color_third_step*2)) -#define buffer_size max_matrix_pixels * 3 / 8 +#define buffer_size (max_matrix_pixels * 3 / 8) class PxMATRIX : public Adafruit_GFX { public: @@ -103,6 +103,7 @@ class PxMATRIX : public Adafruit_GFX { inline void begin(); inline void clearDisplay(void); + inline void clearDisplay(bool selected_buffer); // Updates the display inline void display(uint16_t show_time); @@ -110,15 +111,12 @@ class PxMATRIX : public Adafruit_GFX { // Draw pixels inline void drawPixelRGB565(int16_t x, int16_t y, uint16_t color); - inline void drawPixelRGB565(int16_t x, int16_t y, uint16_t color, bool selected_buffer); inline void drawPixel(int16_t x, int16_t y, uint16_t color); - inline void drawPixel(int16_t x, int16_t y, uint16_t color, bool selected_buffer); inline void drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g,uint8_t b); - inline void drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g,uint8_t b, bool selected_buffer); - // Does nothing for now + // Does nothing for now (always returns 0) uint8_t getPixel(int8_t x, int8_t y); // Converts RGB888 to RGB565 @@ -139,11 +137,8 @@ class PxMATRIX : public Adafruit_GFX { // Helps to reduce display update latency on larger displays inline void setFastUpdate(bool fast_update); - // Select active buffer to updare display from - inline void selectBuffer(bool selected_buffer); - - // Select active buffer to updare display from - inline void swapBuffers(); + // When using double buffering, this displays the draw buffer + inline void showBuffer(); // Control the minimum color values that result in an active pixel inline void setColorOffset(uint8_t r, uint8_t g,uint8_t b); @@ -167,10 +162,9 @@ class PxMATRIX : public Adafruit_GFX { private: // the display buffer for the LED matrix + uint8_t PxMATRIX_buffer[PxMATRIX_COLOR_DEPTH][buffer_size]; #ifdef double_buffer - uint8_t PxMATRIX_buffer[PxMATRIX_COLOR_DEPTH][2*buffer_size];// = {0x00 }; -#else - uint8_t PxMATRIX_buffer[PxMATRIX_COLOR_DEPTH][buffer_size];// = {0x00 }; + uint8_t PxMATRIX_buffer2[PxMATRIX_COLOR_DEPTH][buffer_size]; #endif // GPIO pins @@ -213,7 +207,6 @@ class PxMATRIX : public Adafruit_GFX { uint16_t _send_buffer_size; // This is for double buffering - bool _selected_buffer; bool _active_buffer; // Hols configuration @@ -286,7 +279,6 @@ inline void PxMATRIX::init(uint16_t width, uint16_t height,uint8_t LATCH, uint8_ _rows_per_buffer = _height/2; _panel_width_bytes = (_width/_panels_width)/8; - _selected_buffer=false; _active_buffer=false; _color_R_offset=0; @@ -303,7 +295,10 @@ inline void PxMATRIX::init(uint16_t width, uint16_t height,uint8_t LATCH, uint8_ _scan_pattern=LINE; _driver_chip=SHIFT; - clearDisplay(); + clearDisplay(0); +#ifdef double_buffer + clearDisplay(1); +#endif } @@ -457,26 +452,13 @@ inline PxMATRIX::PxMATRIX(uint16_t width, uint16_t height,uint8_t LATCH, uint8_t } inline void PxMATRIX::drawPixel(int16_t x, int16_t y, uint16_t color) { - drawPixelRGB565( x, y, color,0 ); -} - -inline void PxMATRIX::drawPixel(int16_t x, int16_t y, uint16_t color, bool selected_buffer) { - drawPixelRGB565( x, y, color, selected_buffer); -} - -inline void PxMATRIX::selectBuffer(bool selected_buffer) -{ - - _selected_buffer=selected_buffer; + drawPixelRGB565(x, y, color); } -inline void PxMATRIX::swapBuffers() -{ - - _selected_buffer!=_selected_buffer; +inline void PxMATRIX::showBuffer() { + _active_buffer=!_active_buffer; } - inline void PxMATRIX::setColorOffset(uint8_t r, uint8_t g,uint8_t b) { if ((color_half_step+r)<0) @@ -564,11 +546,7 @@ inline void PxMATRIX::fillMatrixBuffer(int16_t x, int16_t y, uint8_t r, uint8_t if (_scan_pattern!=LINE && _scan_pattern!=WZAGZIG && _scan_pattern!=VZAG) { // Precomputed row offset values -#ifdef double_buffer - base_offset=buffer_size*selected_buffer+_row_offset[y]-(x/8)*2; -#else base_offset=_row_offset[y]-(x/8)*2; -#endif uint8_t row_sector=0; uint16_t row_sector__offset=_width/4; for (uint8_t yy = 0; yy<_height; yy+=2*_row_pattern) @@ -593,9 +571,6 @@ inline void PxMATRIX::fillMatrixBuffer(int16_t x, int16_t y, uint8_t r, uint8_t uint8_t in_row_byte_offset = x_byte%_panel_width_bytes; // this could be pretty easily extended to vertical stacking as well total_offset_r = _row_offset[y] - in_row_byte_offset - _panel_width_bytes*(_row_sets_per_buffer*(_panels_width*which_buffer + which_panel) + vert_index_in_buffer); -#ifdef double_buffer - total_offset_r -= buffer_size*selected_buffer; -#endif } total_offset_g=total_offset_r-_pattern_color_bytes; @@ -605,48 +580,51 @@ inline void PxMATRIX::fillMatrixBuffer(int16_t x, int16_t y, uint8_t r, uint8_t if ((_scan_pattern==ZAGGIZ) && ((y%(_row_pattern*2))<_row_pattern)) bit_select = 7-bit_select; + uint8_t (*PxMATRIX_bufferp)[PxMATRIX_COLOR_DEPTH][buffer_size] = &PxMATRIX_buffer; + +#ifdef double_buffer + PxMATRIX_bufferp = selected_buffer ? &PxMATRIX_buffer2 : &PxMATRIX_buffer; +#endif + //Color interlacing for (int this_color=0; this_color color_tresh+_color_R_offset) - PxMATRIX_buffer[this_color][total_offset_r] |=_BV(bit_select); + (*PxMATRIX_bufferp)[this_color][total_offset_r] |=_BV(bit_select); else - PxMATRIX_buffer[this_color][total_offset_r] &= ~_BV(bit_select); + (*PxMATRIX_bufferp)[this_color][total_offset_r] &= ~_BV(bit_select); if (g > color_tresh+_color_G_offset) - PxMATRIX_buffer[(this_color+color_third_step)%PxMATRIX_COLOR_DEPTH][total_offset_g] |=_BV(bit_select); + (*PxMATRIX_bufferp)[(this_color+color_third_step)%PxMATRIX_COLOR_DEPTH][total_offset_g] |=_BV(bit_select); else - PxMATRIX_buffer[(this_color+color_third_step)%PxMATRIX_COLOR_DEPTH][total_offset_g] &= ~_BV(bit_select); + (*PxMATRIX_bufferp)[(this_color+color_third_step)%PxMATRIX_COLOR_DEPTH][total_offset_g] &= ~_BV(bit_select); if (b > color_tresh+_color_B_offset) - PxMATRIX_buffer[(this_color+color_two_third_step)%PxMATRIX_COLOR_DEPTH][total_offset_b] |=_BV(bit_select); + (*PxMATRIX_bufferp)[(this_color+color_two_third_step)%PxMATRIX_COLOR_DEPTH][total_offset_b] |=_BV(bit_select); else - PxMATRIX_buffer[(this_color+color_two_third_step)%PxMATRIX_COLOR_DEPTH][total_offset_b] &= ~_BV(bit_select); + (*PxMATRIX_bufferp)[(this_color+color_two_third_step)%PxMATRIX_COLOR_DEPTH][total_offset_b] &= ~_BV(bit_select); } } -inline void PxMATRIX::drawPixelRGB565(int16_t x, int16_t y, uint16_t color, bool selected_buffer) { - uint8_t r = ((((color >> 11) & 0x1F) * 527) + 23) >> 6; - uint8_t g = ((((color >> 5) & 0x3F) * 259) + 33) >> 6; - uint8_t b = (((color & 0x1F) * 527) + 23) >> 6; - fillMatrixBuffer( x, y, r, g,b, selected_buffer); -} - inline void PxMATRIX::drawPixelRGB565(int16_t x, int16_t y, uint16_t color) { uint8_t r = ((((color >> 11) & 0x1F) * 527) + 23) >> 6; uint8_t g = ((((color >> 5) & 0x3F) * 259) + 33) >> 6; uint8_t b = (((color & 0x1F) * 527) + 23) >> 6; - fillMatrixBuffer( x, y, r, g,b, 0); -} - -inline void PxMATRIX::drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g,uint8_t b, bool selected_buffer) { - fillMatrixBuffer(x, y, r, g,b, selected_buffer); +#ifdef double_buffer + fillMatrixBuffer(x, y, r, g, b, !_active_buffer); +#else + fillMatrixBuffer(x, y, r, g, b, false); +#endif } inline void PxMATRIX::drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g,uint8_t b) { - fillMatrixBuffer(x, y, r, g,b, 0); +#ifdef double_buffer + fillMatrixBuffer(x, y, r, g, b, !_active_buffer); +#else + fillMatrixBuffer(x, y, r, g, b, false); +#endif } // the most basic function, get a single pixel @@ -842,6 +820,16 @@ void PxMATRIX::display(uint16_t show_time) { #ifdef ESP8266 ESP.wdtFeed(); #endif + +uint8_t (*bufferp)[PxMATRIX_COLOR_DEPTH][buffer_size] = &PxMATRIX_buffer; + +#ifdef double_buffer + if(_active_buffer) + bufferp=&PxMATRIX_buffer2; + else + bufferp=&PxMATRIX_buffer; +#endif + for (uint8_t i=0;i<_row_pattern;i++) { if(_driver_chip == SHIFT) { @@ -861,11 +849,8 @@ void PxMATRIX::display(uint16_t show_time) { digitalWrite(_LATCH_PIN,LOW); delayMicroseconds(1); -#ifdef double_buffer - SPI.writeBytes(&PxMATRIX_buffer[_display_color][buffer_size*_active_buffer+i*_send_buffer_size],_send_buffer_size); -#else - SPI.writeBytes(&PxMATRIX_buffer[_display_color][i*_send_buffer_size],_send_buffer_size); -#endif + SPI.writeBytes(&(*bufferp)[_display_color][i*_send_buffer_size],_send_buffer_size); + while ((micros()-start_time)> (7 - this_byte)) & 1)) GPIO_REG_SET( 1 << SPI_BUS_MOSI); @@ -945,9 +925,6 @@ void PxMATRIX::display(uint16_t show_time) { if (_display_color>=PxMATRIX_COLOR_DEPTH) { _display_color=0; -#ifdef double_buffer - _active_buffer=_selected_buffer; -#endif } } @@ -1037,10 +1014,24 @@ void PxMATRIX::displayTestPixel(uint16_t show_time) { latch(show_time); } -// clear everything void PxMATRIX::clearDisplay(void) { - for(int this_color=0;this_color + +#ifdef ESP32 + +#define P_LAT 22 +#define P_A 19 +#define P_B 23 +#define P_C 18 +#define P_D 5 +#define P_E 15 +#define P_OE 2 +hw_timer_t * timer = NULL; +portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; + +#endif + +#ifdef ESP8266 + +#include +Ticker display_ticker; +#define P_LAT 16 +#define P_A 5 +#define P_B 4 +#define P_C 15 +#define P_D 12 +#define P_E 0 +#define P_OE 2 + +#endif +// Pins for LED MATRIX + +//PxMATRIX display(32,16,P_LAT, P_OE,P_A,P_B,P_C); +PxMATRIX display(64,32,P_LAT, P_OE,P_A,P_B,P_C,P_D); +//PxMATRIX display(64,64,P_LAT, P_OE,P_A,P_B,P_C,P_D,P_E); + +#ifdef ESP8266 +// ISR for display refresh +void display_updater() +{ + display.display(70); +} +#endif + +#ifdef ESP32 +void IRAM_ATTR display_updater(){ + // Increment the counter and set the time of ISR + portENTER_CRITICAL_ISR(&timerMux); + display.display(70); + portEXIT_CRITICAL_ISR(&timerMux); +} +#endif + +struct Text { + const char *text; + uint16_t width, height; + int16_t x, y; + int16_t dx, dy; +} text = {"Hello", 0, 0, 0, 0, 1, 1}; + + +uint16_t textColor = display.color565(0, 0, 255); +uint16_t myBLACK = display.color565(0, 0, 0); +uint16_t lineColor = display.color565(255, 0, 0); +uint16_t backgroundColor = display.color565(0, 255, 0); + +void setup() { + // put your setup code here, to run once: +Serial.begin(9600); + display.begin(16); + display.flushDisplay(); + display.setTextWrap(false); + + #ifdef ESP8266 + display_ticker.attach(0.002, display_updater); + #endif + + #ifdef ESP32 + timer = timerBegin(0, 80, true); + timerAttachInterrupt(timer, &display_updater, true); + timerAlarmWrite(timer, 2000, true); + timerAlarmEnable(timer); + #endif + + int16_t x1 = 0, y1 = 0; + display.getTextBounds(text.text, 0, 0, &x1, &y1, &text.width, &text.height); + text.width-=2; + text.height-=2; +} + +int16_t x=0, dx=1; + +void loop() { +// display.clearDisplay(); + display.fillScreen(backgroundColor); + + if(x+dx>=display.width() || x+dx<0) + dx=-dx; + x+=dx; + display.drawLine(x,0, display.width()-x-1, display.height()-1, lineColor); + + if(text.x+text.dx+text.width>=display.width() || text.x+text.dx<0) + text.dx=-text.dx; + if(text.y+text.dy+text.height>=display.height() || text.y+text.dy<0) + text.dy=-text.dy; + text.x+=text.dx; + text.y+=text.dy; + display.setTextColor(textColor); + display.setCursor(text.x, text.y); + display.print(text.text); + + display.showBuffer(); + delay(20); +}