diff --git a/src/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp b/src/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp index b006e31..e21e36d 100644 --- a/src/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp +++ b/src/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp @@ -30,24 +30,7 @@ * However, if the library is to be used with lower colour depth (i.e. 6 bit colour), then we need to ensure the 8-bit value passed to the colour masking * is adjusted accordingly to ensure the LSB's are shifted left to MSB, by the difference. Otherwise the colours will be all screwed up. */ -// #if PIXEL_COLOR_DEPTH_BITS > 12 -// #error "Color depth bits cannot be greater than 12." -// #elif PIXEL_COLOR_DEPTH_BITS < 2 -// #error "Color depth bits cannot be less than 2." -// #endif - -// #define MASK_OFFSET (16 - PIXEL_COLOR_DEPTH_BITS) -// #define PIXEL_COLOR_MASK_BIT(color_depth_index) (1 << (color_depth_index + MASK_OFFSET)) #define PIXEL_COLOR_MASK_BIT(color_depth_index, mask_offset) (1 << (color_depth_index + mask_offset)) -// static constexpr uint8_t const MASK_OFFSET = 8-PIXEL_COLOUR_DEPTH_BITS; - -/* - #if PIXEL_COLOR_DEPTH_BITS < 8 - uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit colour (8 bits per RGB subpixel) - #else - uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit colour (8 bits per RGB subpixel) - #endif -*/ bool MatrixPanel_I2S_DMA::allocateDMAmemory() { @@ -155,7 +138,7 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory() */ // malloc the DMA linked list descriptors that i2s_parallel will need - desccount = numDMAdescriptorsPerRow * ROWS_PER_FRAME; + int desccount = numDMAdescriptorsPerRow * ROWS_PER_FRAME; if (m_cfg.double_buff) { @@ -174,6 +157,33 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory() } // end allocateDMAmemory() + + +/* +// Version 2.0 March 2023 +int MatrixPanel_I2S_DMA::create_descriptor_links(void *data, size_t size, bool dmadesc_b, bool countonly) +{ + int len = size; + uint8_t *data2 = (uint8_t *)data; + + int n = 0; + while (len) + { + int dmalen = len; + if (dmalen > DMA_MAX) + dmalen = DMA_MAX; + + if (!countonly) + dma_bus.create_dma_desc_link(data2, dmalen, dmadesc_b); + + len -= dmalen; + data2 += dmalen; + n++; + } + + return n; +} +*/ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG &_cfg) { @@ -188,32 +198,37 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG &_cfg) num_dma_payload_colour_depths = 1; } + + #ifdef NO_ROW_SCAN_SHUFFLE + // Fill DMA linked lists for both frames (as in, halves of the HUB75 panel) in sequence (top to bottom) + for (int row = 0; row < ROWS_PER_FRAME; row++) +#else // Create row vector for a row shuffle. std::vector v; - for (int i = 0; i < ROWS_PER_FRAME; i++) { v.push_back(i); } - for (int i = 1; i < ROWS_PER_FRAME-5; i++) - { - std::iter_swap(v.begin()+i,v.begin()+i+5); - } - for (int &row: v) - { - // Serial.println (row, DEC); + + if (ROWS_PER_FRAME == 16) { //64wx32h pixel panel + // v = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + v = {6,2,4,0,8,10,12,15,14,13,11,9,1,5,3,7}; + } + + if (ROWS_PER_FRAME == 32) { //64wx64h panel + // v = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + v = {5,3,1,6,8,11,13,15,17,19,21,23,25,27,31,30,29,28,26,24,22,20,18,16,10,14,12,9,7,0,4,2}; } - // 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 row = 0; row < ROWS_PER_FRAME; row++) for (int &row: v) +#endif { // first set of data is LSB through MSB, single pass (IF TOTAL SIZE < DMA_MAX) - all colour bits are displayed once, which takes care of everything below and including 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, dma_buff.rowBits[row]->getDataPtr(), dma_buff.rowBits[row]->size(num_dma_payload_colour_depths)); // previous_dmadesc_a = &dmadesc_a[current_dmadescriptor_offset]; - dma_bus.create_dma_desc_link(frame_buffer[0].rowBits[row]->getDataPtr(0, 0), frame_buffer[0].rowBits[row]->size(1), false); + dma_bus.create_dma_desc_link(frame_buffer[0].rowBits[row]->getDataPtr(0, 0), frame_buffer[0].rowBits[row]->size(), false); if (m_cfg.double_buff) { - dma_bus.create_dma_desc_link(frame_buffer[1].rowBits[row]->getDataPtr(0, 1), frame_buffer[1].rowBits[row]->size(1), true); + dma_bus.create_dma_desc_link(frame_buffer[1].rowBits[row]->getDataPtr(0, 1), frame_buffer[1].rowBits[row]->size(), true); } current_dmadescriptor_offset++; @@ -478,7 +493,7 @@ void MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint8_t red, uint8_t green, uint * This effectively clears buffers to blank BLACK and makes it ready to display output. * (Brightness control via OE bit manipulation is another case) - this must be done as well seperately! */ -void MatrixPanel_I2S_DMA::clearFrameBuffer(bool _buff_id) +void MatrixPanel_I2S_DMA::clearFrameBuffer() { if (!initialized) return; @@ -489,7 +504,7 @@ void MatrixPanel_I2S_DMA::clearFrameBuffer(bool _buff_id) { --row_idx; - ESP32_I2S_DMA_STORAGE_TYPE *row = fb->rowBits[row_idx]->getDataPtr(0, _buff_id); // set pointer to the HEAD of a buffer holding data for the entire matrix row + ESP32_I2S_DMA_STORAGE_TYPE *row = fb->rowBits[row_idx]->getDataPtr(0, -1); // set pointer to the HEAD of a buffer holding data for the entire matrix row ESP32_I2S_DMA_STORAGE_TYPE abcde = (ESP32_I2S_DMA_STORAGE_TYPE)row_idx; abcde <<= BITS_ADDR_OFFSET; // shift row y-coord to match ABCDE bits in vector from 8 to 12 @@ -561,7 +576,7 @@ void MatrixPanel_I2S_DMA::clearFrameBuffer(bool _buff_id) --colouridx; // switch pointer to a row for a specific colour index - row = fb->rowBits[row_idx]->getDataPtr(colouridx, _buff_id); + row = fb->rowBits[row_idx]->getDataPtr(colouridx, -1); row[ESP32_TX_FIFO_POSITION_ADJUST(fb->rowBits[row_idx]->width - 1)] |= BIT_LAT; // -1 pixel to compensate array index starting at 0 diff --git a/src/ESP32-HUB75-MatrixPanel-I2S-DMA.h b/src/ESP32-HUB75-MatrixPanel-I2S-DMA.h index ae3e8ee..cc6e458 100644 --- a/src/ESP32-HUB75-MatrixPanel-I2S-DMA.h +++ b/src/ESP32-HUB75-MatrixPanel-I2S-DMA.h @@ -29,6 +29,8 @@ // #define NO_CIE1931 +// #define NO_ROW_SCAN_SHUFFLE + /* Physical / Chained HUB75(s) RGB pixel WIDTH and HEIGHT. * * This library has been tested with a 64x32 and 64x64 RGB panels. @@ -135,11 +137,9 @@ struct rowBitStruct const bool double_buff; ESP32_I2S_DMA_STORAGE_TYPE *data; - /** @brief - returns size of row of data vectorfor a SINGLE buff - * size (in bytes) of a vector holding full DMA data for a row of pixels with _dpth colour bits - * a SINGLE buffer only size is accounted, when using double buffers it actually takes twice as much space - * but returned size is for a half of double-buffer - * + /** @brief + * Returns size of row of data vectorfor a SINGLE buff for the number of colour depths rquested + * * default - returns full data vector size for a SINGLE buff * */ @@ -150,8 +150,9 @@ struct rowBitStruct return width * _dpth * sizeof(ESP32_I2S_DMA_STORAGE_TYPE); }; - /** @brief - returns pointer to the row's data vector beginning at pixel[0] for _dpth colour bit - * default - returns pointer to the data vector's head + /** @brief + * Returns pointer to the row's data vector beginning at pixel[0] for _dpth colour bit + * * NOTE: this call might be very slow in loops. Due to poor instruction caching in esp32 it might be required a reread from flash * every loop cycle, better use inlined #define instead in such cases */ @@ -168,21 +169,14 @@ struct rowBitStruct #if defined(SPIRAM_DMA_BUFFER) // data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_aligned_alloc(64, size()+size()*double_buff, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - // No longer have double buffer in the same struct - have a different struct data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_aligned_alloc(64, size(), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); -/* - if (!psramFound()) - { - ESP_LOGE("rowBitStruct", "Requested to use PSRAM / SPIRAM for framebuffer, but it was not detected."); - } -*/ #else // data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_malloc( size()+size()*double_buff, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); // No longer have double buffer in the same struct - have a different struct data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_malloc(size(), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); - // ESP_LOGI("rowBitStruct", "Allocated DMA BitBuffer from regular (and limited) SRAM"); + #endif } ~rowBitStruct() { delete data; } @@ -498,8 +492,7 @@ public: { uint8_t r, g, b; color565to888(color, r, g, b); - startWrite(); - + int16_t w = 1; transform(x, y, w, h); if (h > w) @@ -507,7 +500,6 @@ public: else hlineDMA(x, y, w, r, g, b); - endWrite(); } // rgb888 overload virtual inline void drawFastVLine(int16_t x, int16_t y, int16_t h, uint8_t r, uint8_t g, uint8_t b) @@ -528,7 +520,6 @@ public: { uint8_t r, g, b; color565to888(color, r, g, b); - startWrite(); int16_t h = 1; transform(x, y, w, h); @@ -537,7 +528,6 @@ public: else hlineDMA(x, y, w, r, g, b); - endWrite(); } // rgb888 overload virtual inline void drawFastHLine(int16_t x, int16_t y, int16_t w, uint8_t r, uint8_t g, uint8_t b) @@ -558,18 +548,18 @@ public: { uint8_t r, g, b; color565to888(color, r, g, b); - startWrite(); + transform(x, y, w, h); fillRectDMA(x, y, w, h, r, g, b); - endWrite(); + } // rgb888 overload virtual inline void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t r, uint8_t g, uint8_t b) { - startWrite(); + transform(x, y, w, h); fillRectDMA(x, y, w, h, r, g, b); - endWrite(); + } #endif @@ -620,22 +610,6 @@ public: back_buffer_id ^= 1; - // initialized = true; - - /* - i2s_parallel_set_previous_buffer_not_free(); - // Wait before we allow any writing to the buffer. Stop flicker. - while(i2s_parallel_is_previous_buffer_free() == false) { } - - i2s_parallel_flip_to_buffer(ESP32_I2S_DEVICE, back_buffer_id); - // Flip to other buffer as the backbuffer. - // i.e. Graphic changes happen to this buffer, but aren't displayed until flipDMABuffer() is called again. - back_buffer_id ^= 1; - - i2s_parallel_set_previous_buffer_not_free(); - // Wait before we allow any writing to the buffer. Stop flicker. - while(i2s_parallel_is_previous_buffer_free() == false) { } - */ } /** @@ -649,34 +623,17 @@ public: return; } + fb = &frame_buffer[0]; brightness = b; brtCtrlOEv2(b, 0); if (m_cfg.double_buff) { + fb = &frame_buffer[1]; brtCtrlOEv2(b, 1); } } - // Takes a value that is between 0 and MATRIX_WIDTH-1 - /* - void setPanelBrightness(int b) - { - if (!initialized) - { - ESP_LOGI("setPanelBrightness()", "Tried to set output brightness before begin()"); - return; - } - - // Change to set the brightness of the display, range of 1 to matrixWidth (i.e. 1 - 64) - // brightness = b * PIXELS_PER_ROW / 256; - - brtCtrlOE(b); - if (m_cfg.double_buff) - brtCtrlOE(b, 1); - } - */ - /** * @param uint8_t b - 8-bit brightness value */ @@ -735,17 +692,6 @@ public: dma_bus.dma_transfer_stop(); } - void startWrite() - { - // ESP_LOGI("TAG", "startWrite() called"); - active_gfx_writes++; - } - - void endWrite() - { - active_gfx_writes--; - } - // ------- PROTECTED ------- // those might be useful for child classes, like VirtualMatrixPanel protected: @@ -757,7 +703,7 @@ protected: * This effectively clears buffers to blank BLACK and makes it ready to display output. * (Brightness control via OE bit manipulation is another case) */ - void clearFrameBuffer(bool _buff_id = 0); + void clearFrameBuffer(); /* Update a specific pixel in the DMA buffer to a colour */ void updateMatrixDMABuffer(uint16_t x, uint16_t y, uint8_t red, uint8_t green, uint8_t blue); @@ -770,16 +716,17 @@ protected: */ inline void resetbuffers() { - - // flipDMABuffer(); fb = &frame_buffer[0]; + clearFrameBuffer(); + brtCtrlOEv2(brightness, 0); - clearFrameBuffer(0); // buffer ID is not used - brtCtrlOEv2(brightness, 0); // buffer ID is not used + if (m_cfg.double_buff) { - fb = &frame_buffer[1]; - clearFrameBuffer(1); // buffer ID is not used - brtCtrlOEv2(brightness, 1); // buffer ID is not used + fb = &frame_buffer[1]; + clearFrameBuffer(); + brtCtrlOEv2(brightness, 1); + + } } #ifndef NO_FAST_FUNCTIONS @@ -894,6 +841,7 @@ protected: Bus_Parallel16 dma_bus; private: + // Matrix i2s settings HUB75_I2S_CFG m_cfg; @@ -904,17 +852,9 @@ private: * Since it's dimensions is unknown prior to class initialization, we just declare it here as empty struct and will do all allocations later. * Refer to rowBitStruct to get the idea of it's internal structure */ - // frameStruct dma_buff; - frameStruct frame_buffer[2]; frameStruct *fb; // What framebuffer we are writing pixel changes to? (pointer to either frame_buffer[0] or frame_buffer[1] basically ) - // ESP 32 DMA Linked List descriptor - int desccount = 0; - // lldesc_t * dmadesc_a = {0}; - // lldesc_t * dmadesc_b = {0}; - - int active_gfx_writes = 0; // How many async routines are 'drawing' (writing) to the DMA bit buffer. Function called from Adafruit_GFX draw routines like drawCircle etc. int back_buffer_id = 0; // If using double buffer, which one is NOT active (ie. being displayed) to write too? int brightness = 128; // If you get ghosting... reduce brightness level. ((60/64)*255) seems to be the limit before ghosting on a 64 pixel wide physical panel for some panels. int lsbMsbTransitionBit = 0; // For colour depth calculations diff --git a/src/platforms/esp32/esp32_i2s_parallel_dma.cpp b/src/platforms/esp32/esp32_i2s_parallel_dma.cpp index d309501..7b012bf 100644 --- a/src/platforms/esp32/esp32_i2s_parallel_dma.cpp +++ b/src/platforms/esp32/esp32_i2s_parallel_dma.cpp @@ -582,7 +582,8 @@ bool DRAM_ATTR i2s_parallel_is_previous_buffer_free() { } previousBufferFree = false; - while (i2s_parallel_is_previous_buffer_free() == false) {} + //while (i2s_parallel_is_previous_buffer_free() == false) {} + while (!previousBufferFree);