From 4f5fcf039953cbfbae689f9a433c8f9cd36348c8 Mon Sep 17 00:00:00 2001 From: mrfaptastic <12006953+mrfaptastic@users.noreply.github.com> Date: Tue, 21 Dec 2021 22:19:59 +0000 Subject: [PATCH] Christmas DMA core tweaks Bring back support to change the I2S_NUM_X as a define. Attempt to reduce buffer swap flicker with some additional checks. --- ESP32-HUB75-MatrixPanel-I2S-DMA.cpp | 18 ++- ESP32-HUB75-MatrixPanel-I2S-DMA.h | 23 +++- esp32_i2s_parallel_dma.c | 125 ++++++++++++------ esp32_i2s_parallel_dma.h | 4 +- .../1_SimpleTestShapes/1_SimpleTestShapes.ino | 9 +- examples/README.md | 5 +- 6 files changed, 115 insertions(+), 69 deletions(-) diff --git a/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp b/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp index a82b37f..a9fae2f 100644 --- a/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp +++ b/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp @@ -419,7 +419,7 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg) Serial.println(F("Performing I2S setup:")); #endif - i2s_parallel_config_t cfg = { + i2s_parallel_config_t dma_cfg = { .gpio_bus={_cfg.gpio.r1, _cfg.gpio.g1, _cfg.gpio.b1, _cfg.gpio.r2, _cfg.gpio.g2, _cfg.gpio.b2, _cfg.gpio.lat, _cfg.gpio.oe, _cfg.gpio.a, _cfg.gpio.b, _cfg.gpio.c, _cfg.gpio.d, _cfg.gpio.e, -1, -1, -1}, .gpio_clk=_cfg.gpio.clk, .sample_rate=_cfg.i2sspeed, @@ -428,18 +428,16 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg) .lldesc_a=dmadesc_a, .desccount_b=desccount, .lldesc_b=dmadesc_b, - .clkphase=_cfg.clkphase + .clkphase=_cfg.clkphase, + .int_ena_out_eof=_cfg.double_buff }; - - // Setup I2S - i2s_parallel_driver_install(I2S_NUM_0, &cfg); - //i2s_parallel_setup_without_malloc(&I2S1, &cfg); - - // Start DMA Output - i2s_parallel_send_dma(I2S_NUM_0, &dmadesc_a[0]); + + // Setup I2S + i2s_parallel_driver_install(ESP32_I2S_DEVICE, &dma_cfg); + i2s_parallel_send_dma(ESP32_I2S_DEVICE, &dmadesc_a[0]); #if SERIAL_DEBUG - Serial.println(F("configureDMA(): DMA setup completed on I2S_NUM_0.")); + Serial.println(F("configureDMA(): DMA setup completed on ESP32_I2S_DEVICE.")); #endif } // end initMatrixDMABuff diff --git a/ESP32-HUB75-MatrixPanel-I2S-DMA.h b/ESP32-HUB75-MatrixPanel-I2S-DMA.h index 3ef5713..db508cd 100644 --- a/ESP32-HUB75-MatrixPanel-I2S-DMA.h +++ b/ESP32-HUB75-MatrixPanel-I2S-DMA.h @@ -129,9 +129,10 @@ /***************************************************************************************/ /* Definitions below should NOT be ever changed without rewriting library logic */ -#define ESP32_I2S_DMA_MODE I2S_PARALLEL_WIDTH_16 // From esp32_i2s_parallel_v2.h = 16 bits in parallel +#define ESP32_I2S_DMA_MODE I2S_PARALLEL_WIDTH_16 // From esp32_i2s_parallel_v2.h = 16 bits in parallel #define ESP32_I2S_DMA_STORAGE_TYPE uint16_t // DMA output of one uint16_t at a time. -#define CLKS_DURING_LATCH 0 // Not (yet) used. +#define CLKS_DURING_LATCH 0 // Not (yet) used. +#define ESP32_I2S_DEVICE I2S_NUM_1 // Panel Upper half RGB (numbering according to order in DMA gpio_bus configuration) #define BITS_RGB1_OFFSET 0 // Start point of RGB_X1 bits @@ -316,7 +317,7 @@ struct HUB75_I2S_CFG { LAT_PIN_DEFAULT, OE_PIN_DEFAULT, CLK_PIN_DEFAULT }, shift_driver _drv = SHIFTREG, bool _dbuff = false, - clk_speed _i2sspeed = HZ_20M, + clk_speed _i2sspeed = HZ_10M, uint8_t _latblk = 1, // Anything > 1 seems to cause artefacts on ICS panels bool _clockphase = true, uint8_t _min_refresh_rate = 85 @@ -525,14 +526,22 @@ class MatrixPanel_I2S_DMA { Serial.printf_P(PSTR("Set back buffer to: %d\n"), back_buffer_id); #endif - i2s_parallel_flip_to_buffer(I2S_NUM_0, back_buffer_id); - + 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; + 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) { } + + + + } @@ -585,7 +594,7 @@ class MatrixPanel_I2S_DMA { */ void stopDMAoutput() { resetbuffers(); - i2s_parallel_stop_dma(I2S_NUM_0); + i2s_parallel_stop_dma(ESP32_I2S_DEVICE); } diff --git a/esp32_i2s_parallel_dma.c b/esp32_i2s_parallel_dma.c index 960fab6..ec6e17b 100644 --- a/esp32_i2s_parallel_dma.c +++ b/esp32_i2s_parallel_dma.c @@ -13,6 +13,7 @@ // Header #include "esp32_i2s_parallel_dma.h" +#include "esp32_i2s_parallel_mcu_def.h" #include #include @@ -25,22 +26,28 @@ #include // For I2S state management. -static i2s_parallel_state_t *i2s_state = NULL; +static i2s_parallel_state_t *i2s_state = NULL; // ESP32-S2,S3,C3 only has IS20 // Original ESP32 has two I2S's, but we'll stick with the lowest common denominator. -static i2s_dev_t* I2S = &I2S0; // Device to use for this library, change if you want. + +#ifdef ESP32_ORIG +static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0, &I2S1}; +#else +static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0}; +#endif callback shiftCompleteCallback; void setShiftCompleteCallback(callback f) { shiftCompleteCallback = f; } -volatile bool previousBufferFree = true; +volatile int previousBufferOutputLoopCount = 0; +volatile bool previousBufferFree = true; static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) -#ifdef ESP_ORIG +#ifdef ESP32_ORIG if ( (*(i2s_port_t*)arg) == I2S_NUM_1 ) { // https://www.bogotobogo.com/cplusplus/pointers2_voidpointers_arrays.php //For I2S1 @@ -51,17 +58,25 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) } #else - // Other ESP32 MCU's only have one I2S + // Other ESP32 MCU's only have one I2S SET_PERI_REG_BITS(I2S_INT_CLR_REG(0), I2S_OUT_EOF_INT_CLR_V, 1, I2S_OUT_EOF_INT_CLR_S); -#endif - - // at this point, the previously active buffer is free, go ahead and write to it - previousBufferFree = true; - - if(shiftCompleteCallback) // we've defined a callback function ? - shiftCompleteCallback(); - +#endif +/* + if ( previousBufferOutputLoopCount == 1) + { + // at this point, the previously active buffer is free, go ahead and write to it + previousBufferFree = true; + ////previousBufferOutputLoopCount = 0; + //i2s_parallel_set_previous_buffer_not_free(); + } + else { previousBufferOutputLoopCount++; } +*/ + previousBufferFree = true; + + // if(shiftCompleteCallback) // we've defined a callback function ? + // shiftCompleteCallback(); + } // end irq_hndlr @@ -155,7 +170,7 @@ void link_dma_desc(volatile lldesc_t *dmadesc, volatile lldesc_t *prevdmadesc, v esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* conf) { - port = I2S_NUM_0; /// override. + //port = I2S_NUM_0; /// override. if(port < I2S_NUM_0 || port >= I2S_NUM_MAX) { return ESP_ERR_INVALID_ARG; @@ -176,7 +191,7 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co int irq_source; // Initialize I2S0 peripheral - //if (port == I2S_NUM_0) { + if (port == I2S_NUM_0) { periph_module_reset(PERIPH_I2S0_MODULE); periph_module_enable(PERIPH_I2S0_MODULE); iomux_clock = I2S0O_WS_OUT_IDX; @@ -193,8 +208,12 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co case I2S_PARALLEL_WIDTH_MAX: return ESP_ERR_INVALID_ARG; } - /* - } else { + } +#ifdef ESP32_ORIG + // Can't compile if I2S1 if it doesn't exist with that hardware's IDF.... + else { +// I2S = &I2S1; + periph_module_reset(PERIPH_I2S1_MODULE); periph_module_enable(PERIPH_I2S1_MODULE); iomux_clock = I2S1O_WS_OUT_IDX; @@ -212,12 +231,14 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co return ESP_ERR_INVALID_ARG; } } - */ +#endif + // Setup GPIOs int bus_width = get_bus_width(conf->sample_width); // Setup I2S peripheral - i2s_dev_t* dev = I2S; + i2s_dev_t* dev = I2S[port]; + //dev_reset(dev); // Setup GPIO's @@ -245,7 +266,7 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co #ifdef ESP32_SXXX dev->clkm_conf.clk_sel = 2; // esp32-s2 only - dev->clkm_conf.clk_en = 1; + dev->clkm_conf.clk_en = 1; #endif #ifdef ESP32_ORIG @@ -314,7 +335,7 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co // Device Reset - dev_reset(dev); + dev_reset(dev); dev->conf1.val = 0; dev->conf1.tx_stop_en = 0; @@ -329,25 +350,27 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co state->dmadesc_b = conf->lldesc_b; state->i2s_interrupt_port_arg = port; // need to keep this somewhere in static memory for the ISR - // 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); - - if(err) { - return err; - } - - dev->timing.val = 0; - - - // 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. - dev->int_ena.out_eof = 1; + // 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); + + if(err) { + return err; + } + + + // 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; + } return ESP_OK; } @@ -357,7 +380,7 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co return ESP_ERR_INVALID_ARG; } - i2s_dev_t* dev = I2S; + i2s_dev_t* dev = I2S[port]; // Stop all ongoing DMA operations dev->out_link.stop = 1; @@ -373,7 +396,7 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co return ESP_ERR_INVALID_ARG; } - i2s_dev_t* dev = I2S; + i2s_dev_t* dev = I2S[port]; // Configure DMA burst mode @@ -390,16 +413,23 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co return ESP_OK; } - +/* i2s_dev_t* i2s_parallel_get_dev(i2s_port_t port) { if(port < I2S_NUM_0 || port >= I2S_NUM_MAX) { return NULL; } - return I2S; // HARCODE THIS TO RETURN &I2S0 -} +#ifdef ESP32_ORIG +if (port == I2S_NUM_1) + return &I2S1; +#endif + + return I2S0; // HARCODE THIS TO RETURN &I2S0 +} +*/ // Double buffering flipping // Flip to a buffer: 0 for bufa, 1 for bufb +// dmadesc_a and dmadesc_b point to the same memory if double buffering isn't enabled. void i2s_parallel_flip_to_buffer(i2s_port_t port, int buffer_id) { if (i2s_state == NULL) { @@ -418,9 +448,16 @@ void i2s_parallel_flip_to_buffer(i2s_port_t port, int buffer_id) { i2s_state->dmadesc_b[i2s_state->desccount_b-1].qe.stqe_next = active_dma_chain; // we're still shifting out the buffer, so it shouldn't be written to yet. - previousBufferFree = false; + //previousBufferFree = false; + i2s_parallel_set_previous_buffer_not_free(); } bool i2s_parallel_is_previous_buffer_free() { return previousBufferFree; } + + +void i2s_parallel_set_previous_buffer_not_free() { + previousBufferFree = false; + previousBufferOutputLoopCount = 0; +} diff --git a/esp32_i2s_parallel_dma.h b/esp32_i2s_parallel_dma.h index 2b16e24..1570fe2 100644 --- a/esp32_i2s_parallel_dma.h +++ b/esp32_i2s_parallel_dma.h @@ -41,6 +41,7 @@ typedef struct { int desccount_b; // only used with double buffering lldesc_t * lldesc_b; // only used with double buffering bool clkphase; // Clock signal phase + bool int_ena_out_eof; // Do we raise an interrupt every time the DMA output loops? Don't do this unless we're doing double buffering! } i2s_parallel_config_t; static inline int i2s_parallel_get_memory_width(i2s_port_t port, i2s_parallel_cfg_bits_t width) { @@ -72,7 +73,7 @@ void link_dma_desc(volatile lldesc_t *dmadesc, volatile lldesc_t *prevdmadesc, v esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* conf); esp_err_t i2s_parallel_send_dma(i2s_port_t port, lldesc_t* dma_descriptor); esp_err_t i2s_parallel_stop_dma(i2s_port_t port); -i2s_dev_t* i2s_parallel_get_dev(i2s_port_t port); +//i2s_dev_t* i2s_parallel_get_dev(i2s_port_t port); // For frame buffer flipping / double buffering typedef struct { @@ -83,6 +84,7 @@ typedef struct { void i2s_parallel_flip_to_buffer(i2s_port_t port, int bufid); bool i2s_parallel_is_previous_buffer_free(); +void i2s_parallel_set_previous_buffer_not_free(); // Callback function for when whole length of DMA chain has been sent out. typedef void (*callback)(void); diff --git a/examples/1_SimpleTestShapes/1_SimpleTestShapes.ino b/examples/1_SimpleTestShapes/1_SimpleTestShapes.ino index cfcd768..db3b0d1 100644 --- a/examples/1_SimpleTestShapes/1_SimpleTestShapes.ino +++ b/examples/1_SimpleTestShapes/1_SimpleTestShapes.ino @@ -143,11 +143,11 @@ uint8_t wheelval = 0; void loop() { // animate by going through the colour wheel for the first two lines - //drawText(wheelval); - //wheelval +=1; - - //delay(20); + drawText(wheelval); + wheelval +=1; + delay(20); +/* drawText(0); delay(2000); dma_display->clearScreen(); @@ -161,5 +161,6 @@ void loop() { delay(2000); dma_display->fillScreen(myWHITE); dma_display->clearScreen(); + */ } diff --git a/examples/README.md b/examples/README.md index 9144af8..2acbd79 100644 --- a/examples/README.md +++ b/examples/README.md @@ -8,7 +8,6 @@ |BitmapIcons |Simple example of how to display a bitmap image to the display. | |ChainedPanels |Popular example on how to use the 'VirtualDisplay' class to chain multiple LED Matrix Panels to form a much bigger display! Refer to the README within this example's folder! | |ChainedPanelsAuroraDemo |As above, but showing a large trippy plasma animation. | -|Smooth Double Buffer |Example of using a back-buffer (double buffering). Not useful in 99.9% of use-cases of this library. Uses double the SRAM as well. | -|One_Quarter_1_4_ScanPanel |Using this library with a 32w x 16h 1/4 Scan LED Matrix Panel. Custom co-ordinate remapping logic required. | -|One_Eighth_1_8_ScanPanel |Using this library with a 64w x 32h 1/8 Scan LED Matrix Panel. Custom co-ordinate remapping logic required. +|One_Quarter_1_4_ScanPanel |Using this library with a 32w x 16h 1/4 Scan LED Matrix Panel. Custom co-ordinate remapping logic required. | +|One_Eighth_1_8_ScanPanel |Using this library with a 64w x 32h 1/8 Scan LED Matrix Panel. Custom co-ordinate remapping logic required. |PIO_TestPatterns |Non-Arduino example of how to display basic shapes. |