diff --git a/src/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp b/src/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp index c049c73..7d6b898 100644 --- a/src/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp +++ b/src/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp @@ -8,15 +8,42 @@ static const char* TAG = "MatrixPanel"; */ #define getRowDataPtr(row, _dpth, buff_id) &(dma_buff.rowBits[row]->data[_dpth * dma_buff.rowBits[row]->width + buff_id*(dma_buff.rowBits[row]->width * dma_buff.rowBits[row]->colour_depth)]) -// We need to update the correct uint16_t in the rowBitStruct array, that gets sent out in parallel -// 16 bit parallel mode - Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering -// Irrelevant for ESP32-S2 the way the FIFO ordering works is different - refer to page 679 of S2 technical reference manual +/* We need to update the correct uint16_t in the rowBitStruct array, that gets sent out in parallel + * 16 bit parallel mode - Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering + * Irrelevant for ESP32-S2 the way the FIFO ordering works is different - refer to page 679 of S2 technical reference manual + */ #if defined (ESP32_THE_ORIG) - #define ESP32_TX_FIFO_POSITION_ADJUST(x_coord) (x_coord & 1U ? (x_coord-1):(x_coord+1)) + #define ESP32_TX_FIFO_POSITION_ADJUST(x_coord) ((x_coord & 1U) ? (x_coord-1):(x_coord+1)) #else #define ESP32_TX_FIFO_POSITION_ADJUST(x_coord) x_coord #endif +/* This library is designed to take an 8 bit / 1 byte value (0-255) for each R G B colour sub-pixel. + * The PIXEL_COLOUR_DEPTH_BITS should always be '8' as a result. + * 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_COLOUR_DEPTH_BITS > 8 + #error "Color depth bits cannot be greater than 8." +#elif PIXEL_COLOUR_DEPTH_BITS < 2 + #error "Colour depth bits cannot be less than 2." +#endif + +#if PIXEL_COLOUR_DEPTH_BITS != 8 + #define MASK_OFFSET (8 - PIXEL_COLOUR_DEPTH_BITS) + #define PIXEL_COLOUR_MASK_BIT(colour_depth_index) (1 << (colour_depth_index + MASK_OFFSET)) + //static constexpr uint8_t const MASK_OFFSET = 8-PIXEL_COLOUR_DEPTH_BITS; +#else + #define PIXEL_COLOUR_MASK_BIT(colour_depth_index) (1 << (colour_depth_index)) +#endif + +/* + #if PIXEL_COLOUR_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 color (8 bits per RGB subpixel) + #endif +*/ bool MatrixPanel_I2S_DMA::allocateDMAmemory() @@ -38,9 +65,11 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory() if (ptr->data == nullptr) { - ESP_LOGE(TAG, "CRITICAL ERROR: Can't allocate rowBitStruct %d! Not enough memory for requested PIXEL_COLOUR_DEPTH_BITS. Please reduce PIXEL_COLOUR_DEPTH_BITS value.\r\n", malloc_num); - return false; - // TODO: should we release all previous rowBitStructs here??? + ESP_LOGE(TAG, "CRITICAL ERROR: Not enough memory for requested colour depth! Please reduce PIXEL_COLOUR_DEPTH_BITS value.\r\n"); + ESP_LOGE(TAG, "Could not allocate rowBitStruct %d!.\r\n", malloc_num); + + return false; + // TODO: should we release all previous rowBitStructs here??? } allocated_fb_memory += ptr->size(); @@ -199,8 +228,7 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg) // Setup DMA and Output to GPIO // auto bus_cfg = dma_bus.config(); // バス設定用の構造体を取得します。 - - //bus_cfg.i2s_port = I2S_NUM_0; // 使用するI2Sポートを選択 (I2S_NUM_0 or I2S_NUM_1) (ESP32のI2S LCDモードを使用します) + bus_cfg.bus_freq = _cfg.i2sspeed; bus_cfg.pin_wr = m_cfg.gpio.clk; // WR を接続しているピン番号 @@ -227,6 +255,7 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg) dma_bus.dma_transfer_start(); + flipDMABuffer(); // display back buffer 0, draw to 1, ignored if double buffering isn't enabled. //i2s_parallel_send_dma(ESP32_I2S_DEVICE, &dmadesc_a[0]); ESP_LOGI(TAG, "DMA setup completed"); @@ -309,12 +338,15 @@ void IRAM_ATTR MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint16_t x_coord, uint uint8_t colour_depth_idx = PIXEL_COLOUR_DEPTH_BITS; do { --colour_depth_idx; +/* // uint8_t mask = (1 << (colour_depth_idx COLOR_DEPTH_LESS_THAN_8BIT_ADJUST)); // expect 24 bit colour (8 bits per RGB subpixel) #if PIXEL_COLOUR_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 color (8 bits per RGB subpixel) #endif +*/ + uint8_t mask = PIXEL_COLOUR_MASK_BIT(colour_depth_idx); uint16_t RGB_output_bits = 0; /* Per the .h file, the order of the output RGB bits is: @@ -357,11 +389,13 @@ void MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint8_t red, uint8_t green, uint // let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer uint16_t RGB_output_bits = 0; // uint8_t mask = (1 << colour_depth_idx COLOR_DEPTH_LESS_THAN_8BIT_ADJUST); // 24 bit colour - #if PIXEL_COLOUR_DEPTH_BITS < 8 - uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit color (8 bits per RGB subpixel) - #else - uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit colour (8 bits per RGB subpixel) - #endif + // #if PIXEL_COLOUR_DEPTH_BITS < 8 + // uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit color (8 bits per RGB subpixel) + // #else + // uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit colour (8 bits per RGB subpixel) + // #endif + + uint8_t mask = PIXEL_COLOUR_MASK_BIT(colour_depth_idx); /* Per the .h file, the order of the output RGB bits is: * BIT_B2, BIT_G2, BIT_R2, BIT_B1, BIT_G1, BIT_R1 */ @@ -693,11 +727,12 @@ void MatrixPanel_I2S_DMA::hlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, // let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer uint16_t RGB_output_bits = 0; // uint8_t mask = (1 << colour_depth_idx COLOR_DEPTH_LESS_THAN_8BIT_ADJUST); - #if PIXEL_COLOUR_DEPTH_BITS < 8 - uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit color (8 bits per RGB subpixel) - #else - uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit color (8 bits per RGB subpixel) - #endif + // #if PIXEL_COLOUR_DEPTH_BITS < 8 + // uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit color (8 bits per RGB subpixel) + // #else + // uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit color (8 bits per RGB subpixel) + // #endif + uint8_t mask = PIXEL_COLOUR_MASK_BIT(colour_depth_idx); /* Per the .h file, the order of the output RGB bits is: * BIT_B2, BIT_G2, BIT_R2, BIT_B1, BIT_G1, BIT_R1 */ @@ -717,14 +752,17 @@ void MatrixPanel_I2S_DMA::hlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, int16_t _l = l; do { // iterate pixels in a row int16_t _x = x_coord + --_l; - -#if defined(ESP32_THE_ORIG) - // Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering - uint16_t &v = p[_x & 1U ? --_x : ++_x]; -#else - // ESP 32 doesn't need byte flipping for TX FIFO. - uint16_t &v = p[_x]; -#endif + + /* + #if defined(ESP32_THE_ORIG) + // Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering + uint16_t &v = p[_x & 1U ? --_x : ++_x]; + #else + // ESP 32 doesn't need byte flipping for TX FIFO. + uint16_t &v = p[_x]; + #endif + */ + uint16_t &v = p[ESP32_TX_FIFO_POSITION_ADJUST(_x)]; v &= _colourbitclear; // reset color bits v |= RGB_output_bits; // set new color bits @@ -759,10 +797,13 @@ void MatrixPanel_I2S_DMA::vlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, blue = lumConvTab[blue]; #endif +/* #if defined(ESP32_THE_ORIG) // Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering x_coord & 1U ? --x_coord : ++x_coord; #endif +*/ + x_coord = ESP32_TX_FIFO_POSITION_ADJUST(x_coord); uint8_t colour_depth_idx = PIXEL_COLOUR_DEPTH_BITS; do { // Iterating through color depth bits (8 iterations) @@ -770,11 +811,13 @@ void MatrixPanel_I2S_DMA::vlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, // let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer // uint8_t mask = (1 << colour_depth_idx COLOR_DEPTH_LESS_THAN_8BIT_ADJUST); - #if PIXEL_COLOUR_DEPTH_BITS < 8 - uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit color (8 bits per RGB subpixel) - #else - uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit color (8 bits per RGB subpixel) - #endif + // #if PIXEL_COLOUR_DEPTH_BITS < 8 + // uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit color (8 bits per RGB subpixel) + // #else + // uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit color (8 bits per RGB subpixel) + // #endif + + uint8_t mask = PIXEL_COLOUR_MASK_BIT(colour_depth_idx); uint16_t RGB_output_bits = 0; /* Per the .h file, the order of the output RGB bits is: diff --git a/src/ESP32-HUB75-MatrixPanel-I2S-DMA.h b/src/ESP32-HUB75-MatrixPanel-I2S-DMA.h index 425f850..f8eb043 100644 --- a/src/ESP32-HUB75-MatrixPanel-I2S-DMA.h +++ b/src/ESP32-HUB75-MatrixPanel-I2S-DMA.h @@ -8,16 +8,8 @@ #include //#include - -//#include "freertos/FreeRTOS.h" -//#include "freertos/task.h" -//#include "freertos/semphr.h" -//#include "freertos/queue.h" - -//#include "esp_heap_caps.h" #include "platforms/platform_detect.hpp" - #ifdef USE_GFX_ROOT #include #include "GFX.h" // Adafruit GFX core class -> https://github.com/mrfaptastic/GFX_Root @@ -119,23 +111,6 @@ // Max clock cycles to blank OE before/after LAT signal change #define MAX_LAT_BLANKING 4 -/***************************************************************************************/ -// Check compile-time only options -#if PIXEL_COLOUR_DEPTH_BITS > 8 - #error "Pixel color depth bits cannot be greater than 8." -#elif PIXEL_COLOUR_DEPTH_BITS < 2 - #error "Pixel color depth bits cannot be less than 2." -#endif - -/* This library is designed to take an 8 bit / 1 byte value (0-255) for each R G B colour sub-pixel. - * The PIXEL_COLOUR_DEPTH_BITS should always be '8' as a result. - * 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_COLOUR_DEPTH_BITS != 8 -static constexpr uint8_t const MASK_OFFSET = 8-PIXEL_COLOUR_DEPTH_BITS; -#endif - /***************************************************************************************/ /** @brief - Structure holds raw DMA data to drive TWO full rows of pixels spanning through all chained modules @@ -406,7 +381,7 @@ class MatrixPanel_I2S_DMA { /** * A wrapper to fill whatever selected DMA buffer / screen with black */ - inline void clearScreen(){ clearFrameBuffer(back_buffer_id); /*updateMatrixDMABuffer(0,0,0);*/ }; + inline void clearScreen(){ startWrite(); clearFrameBuffer(back_buffer_id); endWrite(); /*updateMatrixDMABuffer(0,0,0);*/ }; #ifndef NO_FAST_FUNCTIONS /** @@ -416,7 +391,9 @@ class MatrixPanel_I2S_DMA { virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color){ uint8_t r, g, b; color565to888(color, r, g, b); + startWrite(); vlineDMA(x, y, h, 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){ vlineDMA(x, y, h, r, g, b); }; @@ -428,7 +405,9 @@ class MatrixPanel_I2S_DMA { virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color){ uint8_t r, g, b; color565to888(color, r, g, b); + startWrite(); 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){ hlineDMA(x, y, w, r, g, b); }; @@ -440,10 +419,16 @@ class MatrixPanel_I2S_DMA { virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color){ uint8_t r, g, b; color565to888(color, r, g, b); + startWrite(); 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){fillRectDMA(x, y, w, h, r, g, b);} + 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(); + fillRectDMA(x, y, w, h, r, g, b); + endWrite(); + } #endif void fillScreenRGB888(uint8_t r, uint8_t g, uint8_t b); @@ -474,22 +459,14 @@ class MatrixPanel_I2S_DMA { static void color565to888(const uint16_t color, uint8_t &r, uint8_t &g, uint8_t &b); - inline void IRAM_ATTR flipDMABuffer() + inline void flipDMABuffer() { - if ( !m_cfg.double_buff) return; + if ( !m_cfg.double_buff) { return; } - //ESP_LOGI("flipDMABuffer()", "Set back buffer to: %d", back_buffer_id); - - if (back_buffer_id) - { - dma_bus.set_dma_output_buffer( true ); - back_buffer_id = 0; - } - else - { - dma_bus.set_dma_output_buffer( false ); - back_buffer_id = 1; - } + // while (active_gfx_writes) { } // wait a bit ? + // initialized = false; + dma_bus.flip_dma_output_buffer( back_buffer_id ); + // initialized = true; /* i2s_parallel_set_previous_buffer_not_free(); @@ -506,8 +483,6 @@ class MatrixPanel_I2S_DMA { while(i2s_parallel_is_previous_buffer_free() == false) { } */ - - } inline void setPanelBrightness(int b) @@ -562,8 +537,16 @@ class MatrixPanel_I2S_DMA { //i2s_parallel_stop_dma(ESP32_I2S_DEVICE); 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 @@ -645,6 +628,7 @@ class MatrixPanel_I2S_DMA { // Other private variables bool initialized = false; + 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 = 32; // If you get ghosting... reduce brightness level. 60 seems to be the limit before ghosting on a 64 pixel wide physical panel for some panels. int lsbMsbTransitionBit = 0; // For colour depth calculations @@ -692,6 +676,8 @@ class MatrixPanel_I2S_DMA { void brtCtrlOE(int brt, const bool _buff_id=0); + + }; // end Class header /***************************************************************************************/ diff --git a/src/platforms/esp32/esp32_i2s_parallel_dma.cpp b/src/platforms/esp32/esp32_i2s_parallel_dma.cpp index 8e1e209..2bb6eba 100644 --- a/src/platforms/esp32/esp32_i2s_parallel_dma.cpp +++ b/src/platforms/esp32/esp32_i2s_parallel_dma.cpp @@ -56,14 +56,36 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) } // end irq_hndlr */ + volatile int active_dma_buffer_output_count = 0; + + static void IRAM_ATTR irq_hndlr(void* arg) { + + // Clear flag so we can get retriggered + SET_PERI_REG_BITS(I2S_INT_CLR_REG(ESP32_I2S_DEVICE), I2S_OUT_EOF_INT_CLR_V, 1, I2S_OUT_EOF_INT_CLR_S); + + active_dma_buffer_output_count++; + +/* + if ( active_dma_buffer_output_count++ ) + { + // Disable DMA chain EOF interrupt until next requested flipbuffer. + // Otherwise we're needlessly generating interrupts we don't care about. + //SET_PERI_REG_BITS(I2S_INT_ENA_REG(ESP32_I2S_DEVICE), I2S_OUT_EOF_INT_ENA_V, 0, I2S_OUT_EOF_INT_ENA_S); + active_dma_buffer_output_count = 0; + } + */ + + } // end irq_hndlr + // Static - static i2s_dev_t* getDev(int port) + static i2s_dev_t* getDev() { #if defined (CONFIG_IDF_TARGET_ESP32S2) return &I2S0; #else - return (port == 0) ? &I2S0 : &I2S1; + return (ESP32_I2S_DEVICE == 0) ? &I2S0 : &I2S1; #endif + } // Static @@ -78,23 +100,18 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) } } - void Bus_Parallel16::config(const config_t& cfg) { ESP_LOGI(TAG, "Performing config for ESP32 or ESP32-S2"); _cfg = cfg; - auto port = cfg.port; - _dev = getDev(port); + auto port = ESP32_I2S_DEVICE; //cfg.port; + _dev = getDev(); } bool Bus_Parallel16::init(void) // The big one that gets everything setup. { ESP_LOGI(TAG, "Performing DMA bus init() for ESP32 or ESP32-S2"); - if(_cfg.port < I2S_NUM_0 || _cfg.port >= I2S_NUM_MAX) { - return false; - } - if(_cfg.parallel_width < 8 || _cfg.parallel_width >= 24) { return false; } @@ -105,7 +122,7 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) int irq_source; // Initialize I2S0 peripheral - if (_cfg.port == 0) + if (ESP32_I2S_DEVICE == I2S_NUM_0) { periph_module_reset(PERIPH_I2S0_MODULE); periph_module_enable(PERIPH_I2S0_MODULE); @@ -232,7 +249,7 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) // Must be ESP32 original #if !defined (CONFIG_IDF_TARGET_ESP32S2) dev->clkm_conf.clka_en=0; // Use the 80mhz system clock (PLL_D2_CLK) when '0' - dev->clkm_conf.clkm_div_num = 3; // Hard code to whatever frequency this is. 26Mhz? + dev->clkm_conf.clkm_div_num = 3; // Hard code to 3 whatever frequency this is. 26Mhz? #endif /* @@ -357,50 +374,27 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) dev->conf1.val = 0; dev->conf1.tx_stop_en = 0; -/* - // Allocate I2S status structure for buffer swapping stuff - i2s_state = (i2s_parallel_state_t*) malloc(sizeof(i2s_parallel_state_t)); - assert(i2s_state != NULL); - i2s_parallel_state_t *state = i2s_state; - - state->desccount_a = conf->desccount_a; - state->desccount_b = conf->desccount_b; - state->dmadesc_a = conf->lldesc_a; - state->dmadesc_b = conf->lldesc_b; - state->i2s_interrupt_port_arg = port; // need to keep this somewhere in static memory for the ISR -*/ - dev->timing.val = 0; - //dev->int_ena.out_eof = 1 + + /* If we have double buffering, then allocate an interrupt service routine function + * that can be used for I2S0/I2S1 created interrupts. + */ + if (_double_dma_buffer) { -/* - 12.6.2 DMA Interrupts - • I2S_OUT_TOTAL_EOF_INT: Triggered when all transmitting linked lists are used up. - • I2S_OUT_EOF_INT: Triggered when rxlink has finished sending a packet - -*/ -/* - // We using the double buffering switch logic? - if (conf->int_ena_out_eof) - { // Get ISR setup esp_err_t err = esp_intr_alloc(irq_source, (int)(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1), - irq_hndlr, - &state->i2s_interrupt_port_arg, NULL); + irq_hndlr, NULL, NULL); if(err) { - return err; + ESP_LOGE(TAG, "init() Failed to setup interrupt request handeler."); + return false; } - - // Setup interrupt handler which is focussed only on the (page 322 of Tech. Ref. Manual) - // "I2S_OUT_EOF_INT: Triggered when rxlink has finished sending a packet" - // ... whatever the hell that is supposed to mean... One massive linked list? So all pixels in the chain? - dev->int_ena.out_eof = 1; + // Don't do this here. Don't enable just yet. + // dev->int_ena.out_eof = 1; } -*/ #if defined (CONFIG_IDF_TARGET_ESP32S2) @@ -442,6 +436,7 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) if (_dmadesc_a) heap_caps_free(_dmadesc_a); // free all dma descrptios previously _dmadesc_count = len; + _dmadesc_last = len-1; ESP_LOGI(TAG, "Allocating memory for %d DMA descriptors.", len); @@ -474,7 +469,17 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) _dmadesc_b_idx = 0; ESP_LOGD(TAG, "Allocating %d bytes of memory for DMA descriptors.", sizeof(HUB75_DMA_DESCRIPTOR_T) * len); - + + // New - Temporary blank descriptor for transitions between DMA buffer + _dmadesc_blank = (HUB75_DMA_DESCRIPTOR_T*)heap_caps_malloc(sizeof(HUB75_DMA_DESCRIPTOR_T) * 1, MALLOC_CAP_DMA); + _dmadesc_blank->size = 1024*2; + _dmadesc_blank->length = 1024*2; + _dmadesc_blank->buf = (uint8_t*) _blank_data; + _dmadesc_blank->eof = 1; + _dmadesc_blank->sosf = 0; + _dmadesc_blank->owner = 1; + _dmadesc_blank->qe.stqe_next = (lldesc_t*) _dmadesc_blank; + _dmadesc_blank->offset = 0; return true; @@ -516,19 +521,19 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) { dmadesc = &_dmadesc_b[_dmadesc_b_idx]; - next = (_dmadesc_b_idx < (_dmadesc_count-1) ) ? &_dmadesc_b[_dmadesc_b_idx+1]:_dmadesc_b; - eof = (_dmadesc_b_idx == (_dmadesc_count-1)); + next = (_dmadesc_b_idx < (_dmadesc_last) ) ? &_dmadesc_b[_dmadesc_b_idx+1]:_dmadesc_b; + eof = (_dmadesc_b_idx == (_dmadesc_last)); } else { dmadesc = &_dmadesc_a[_dmadesc_a_idx]; // https://stackoverflow.com/questions/47170740/c-negative-array-index - next = (_dmadesc_a_idx < (_dmadesc_count-1) ) ? _dmadesc_a + _dmadesc_a_idx+1:_dmadesc_a; - eof = (_dmadesc_a_idx == (_dmadesc_count-1)); + next = (_dmadesc_a_idx < (_dmadesc_last) ) ? _dmadesc_a + _dmadesc_a_idx+1:_dmadesc_a; + eof = (_dmadesc_a_idx == (_dmadesc_last)); } - if ( _dmadesc_a_idx == (_dmadesc_count-1) ) { + if ( _dmadesc_a_idx == (_dmadesc_last) ) { ESP_LOGW(TAG, "Creating final DMA descriptor and linking back to 0."); } @@ -556,7 +561,7 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) // Configure DMA burst mode dev->lc_conf.val = I2S_OUT_DATA_BURST_EN | I2S_OUTDSCR_BURST_EN; - // Set address of DMA descriptor + // Set address of DMA descriptor, start with buffer 0 / 'a' dev->out_link.addr = (uint32_t) _dmadesc_a; // Start DMA operation @@ -581,23 +586,57 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) } // end - void Bus_Parallel16::set_dma_output_buffer(bool dmadesc_b) + void Bus_Parallel16::flip_dma_output_buffer(int ¤t_back_buffer_id) // pass by reference so we can change in main matrixpanel class { - if ( _double_dma_buffer == false) return; + // Setup interrupt handler which is focussed only on the (page 322 of Tech. Ref. Manual) + // "I2S_OUT_EOF_INT: Triggered when rxlink has finished sending a packet" (when dma linked list with eof = 1 is hit) + //_dev->int_ena.out_eof = 1; + + // MISALIGNMENT ON TOP/2 THE PANEL BETWEEN FAST MOVING GRAPHICS IS CAUSED + // DUE TO THE CHANGE OF BACK BUFFER ID AND THE FRIGGIN DMA BUFFER!!! - if ( dmadesc_b == true) // change across to everything 'b'' - { - _dmadesc_a[_dmadesc_count-1].qe.stqe_next = &_dmadesc_b[0]; - _dmadesc_b[_dmadesc_count-1].qe.stqe_next = &_dmadesc_b[0]; - } - else - { - _dmadesc_a[_dmadesc_count-1].qe.stqe_next = &_dmadesc_a[0]; - _dmadesc_b[_dmadesc_count-1].qe.stqe_next = &_dmadesc_a[0]; - } +/* + if ( current_back_buffer_id == 1) { + _dmadesc_a[_dmadesc_last].qe.stqe_next = _dmadesc_blank; + } + else { + _dmadesc_b[_dmadesc_last].qe.stqe_next = _dmadesc_blank; + } +*/ + + // THIS WORKS SMOOTHLY EXCEPT FOR THE OFFSET ON MOVING GRAPHICS + _dev->int_ena.out_eof = 1; + + // Wait until we're now stuck in a _dmadesc_a loop; + active_dma_buffer_output_count = 0; + while (!active_dma_buffer_output_count) {} + + if ( current_back_buffer_id == 1) { + + _dmadesc_a[_dmadesc_last].qe.stqe_next = &_dmadesc_b[0]; + + // Wait until we're now stuck in a _dmadesc_a loop; + active_dma_buffer_output_count = 0; + while (!active_dma_buffer_output_count) {} + + _dmadesc_a[_dmadesc_last].qe.stqe_next = &_dmadesc_a[0]; // get this preped for the next flip buffer + + } else { + _dmadesc_b[_dmadesc_last].qe.stqe_next = &_dmadesc_a[0]; + + // Wait until we're now stuck in a _dmadesc_a loop; + active_dma_buffer_output_count = 0; + while (!active_dma_buffer_output_count) {} + + _dmadesc_b[_dmadesc_last].qe.stqe_next = &_dmadesc_b[0]; + } + current_back_buffer_id ^= 1; + + + + // Disable intterupt + _dev->int_ena.out_eof = 0; - //_dmadesc_a_active ^= _dmadesc_a_active; - } // end flip diff --git a/src/platforms/esp32/esp32_i2s_parallel_dma.hpp b/src/platforms/esp32/esp32_i2s_parallel_dma.hpp index 7fae0e6..151558b 100644 --- a/src/platforms/esp32/esp32_i2s_parallel_dma.hpp +++ b/src/platforms/esp32/esp32_i2s_parallel_dma.hpp @@ -49,9 +49,16 @@ Contributors: #define DMA_MAX (4096-4) +#define ESP32_I2S_DEVICE I2S_NUM_0 + // The type used for this SoC #define HUB75_DMA_DESCRIPTOR_T lldesc_t +//---------------------------------------------------------------------------- + +static void IRAM_ATTR irq_hndlr(void* arg); +static i2s_dev_t* getDev(); + //---------------------------------------------------------------------------- class Bus_Parallel16 @@ -64,8 +71,6 @@ Contributors: struct config_t { - int port = 0; - // max 20MHz (when in 16 bit / 2 byte mode) uint32_t bus_freq = 10000000; int8_t pin_wr = -1; // @@ -112,7 +117,7 @@ Contributors: void dma_transfer_start(); void dma_transfer_stop(); - void set_dma_output_buffer(bool dmadesc_b = false); + void flip_dma_output_buffer(int ¤t_back_buffer_id); private: @@ -124,6 +129,7 @@ Contributors: //bool _dmadesc_a_active = true; uint32_t _dmadesc_count = 0; // number of dma decriptors + uint32_t _dmadesc_last = 0; uint32_t _dmadesc_a_idx = 0; uint32_t _dmadesc_b_idx = 0; @@ -131,6 +137,9 @@ Contributors: HUB75_DMA_DESCRIPTOR_T* _dmadesc_a = nullptr; HUB75_DMA_DESCRIPTOR_T* _dmadesc_b = nullptr; + HUB75_DMA_DESCRIPTOR_T* _dmadesc_blank = nullptr; + uint16_t _blank_data[1024] = {0}; + volatile i2s_dev_t* _dev; diff --git a/src/platforms/esp32s3/gdma_lcd_parallel16.cpp b/src/platforms/esp32s3/gdma_lcd_parallel16.cpp index 87f4058..ce41404 100644 --- a/src/platforms/esp32s3/gdma_lcd_parallel16.cpp +++ b/src/platforms/esp32s3/gdma_lcd_parallel16.cpp @@ -51,7 +51,7 @@ return true; } - static lcd_cam_dev_t* getDev(int port) + static lcd_cam_dev_t* getDev() { return &LCD_CAM; } @@ -61,8 +61,8 @@ void Bus_Parallel16::config(const config_t& cfg) { _cfg = cfg; - auto port = cfg.port; - _dev = getDev(port); + //auto port = cfg.port; + _dev = getDev(); } @@ -402,12 +402,12 @@ } // end - void Bus_Parallel16::set_dma_output_buffer(bool dmadesc_b) + void Bus_Parallel16::flip_dma_output_buffer(int ¤t_back_buffer_id) { - if ( _double_dma_buffer == false) return; + // if ( _double_dma_buffer == false) return; - if ( dmadesc_b == true) // change across to everything 'b'' + if ( current_back_buffer_id == 1) // change across to everything 'b'' { _dmadesc_a[_dmadesc_count-1].next = (dma_descriptor_t *) &_dmadesc_b[0]; _dmadesc_b[_dmadesc_count-1].next = (dma_descriptor_t *) &_dmadesc_b[0]; @@ -418,6 +418,8 @@ _dmadesc_b[_dmadesc_count-1].next = (dma_descriptor_t *) &_dmadesc_a[0]; } + current_back_buffer_id ^= 1; + } // end flip diff --git a/src/platforms/esp32s3/gdma_lcd_parallel16.hpp b/src/platforms/esp32s3/gdma_lcd_parallel16.hpp index aaff669..ac1880b 100644 --- a/src/platforms/esp32s3/gdma_lcd_parallel16.hpp +++ b/src/platforms/esp32s3/gdma_lcd_parallel16.hpp @@ -98,7 +98,7 @@ struct config_t { // LCD_CAM peripheral number. No need to change (only 0 for ESP32-S3.) - int port = 0; + //int port = 0; // max 40MHz (when in 16 bit / 2 byte mode) uint32_t bus_freq = 20000000; @@ -146,7 +146,7 @@ void dma_transfer_start(); void dma_transfer_stop(); - void set_dma_output_buffer(bool dmadesc_b = false); + void flip_dma_output_buffer(int ¤t_back_buffer_id); private: