From 15eaa3e9d394252190eec44b42e555e98a7c4c57 Mon Sep 17 00:00:00 2001 From: Emil Muratov Date: Fri, 19 Feb 2021 12:08:15 +0300 Subject: [PATCH 1/4] Clock phase toggling option, required to support MBI5124 chips Closes #75 Signed-off-by: Emil Muratov --- ESP32-HUB75-MatrixPanel-I2S-DMA.cpp | 9 ++++++++- ESP32-HUB75-MatrixPanel-I2S-DMA.h | 22 +++++++++++++++++++--- esp32_i2s_parallel_v2.c | 5 ++++- esp32_i2s_parallel_v2.h | 1 + 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp b/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp index 6525ef4..2e08af0 100644 --- a/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp +++ b/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp @@ -421,7 +421,8 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg) .desccount_a=desccount, .lldesc_a=dmadesc_a, .desccount_b=desccount, - .lldesc_b=dmadesc_b + .lldesc_b=dmadesc_b, + .clkphase=_cfg.clkphase }; // Setup I2S @@ -656,6 +657,12 @@ void MatrixPanel_I2S_DMA::shiftDriver(const HUB75_I2S_CFG& _cfg){ CLK_PULSE } break; + case HUB75_I2S_CFG::MBI5124: + /* MBI5124 chips must be clocked with positive-edge, since it's LAT signal + * resets on clock's rising edge while high + * https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/files/5952216/5a542453754da.pdf + */ + m_cfg.clkphase=true; case HUB75_I2S_CFG::SHIFT: default: break; diff --git a/ESP32-HUB75-MatrixPanel-I2S-DMA.h b/ESP32-HUB75-MatrixPanel-I2S-DMA.h index c17a08d..81cfd95 100644 --- a/ESP32-HUB75-MatrixPanel-I2S-DMA.h +++ b/ESP32-HUB75-MatrixPanel-I2S-DMA.h @@ -227,7 +227,7 @@ struct HUB75_I2S_CFG { * Enumeration of hardware-specific chips * used to drive matrix modules */ - enum shift_driver {SHIFT=0, FM6124, FM6126A, ICN2038S}; + enum shift_driver {SHIFT=0, FM6124, FM6126A, ICN2038S, MBI5124}; /** * I2S clock speed selector @@ -258,6 +258,20 @@ struct HUB75_I2S_CFG { // How many clock cycles to blank OE before/after LAT signal change, default is 1 clock uint8_t latch_blanking; + /** + * I2S clock phase + * 0 (default) - data lines are clocked with negative edge + * Clk /¯\_/¯\_/ + * LAT __/¯¯¯\__ + * EO ¯¯¯¯¯¯\___ + * + * 1 - data lines are clocked with positive edge + * Clk \_/¯\_/¯\ + * LAT __/¯¯¯\__ + * EO ¯¯¯¯¯¯\__ + * + */ + bool clkphase; // struct constructor HUB75_I2S_CFG ( @@ -271,14 +285,16 @@ struct HUB75_I2S_CFG { shift_driver _drv = SHIFT, bool _dbuff = false, clk_speed _i2sspeed = HZ_10M, - uint16_t _latblk = 1 + uint16_t _latblk = 1, + bool _clockphase = false ) : mx_width(_w), mx_height(_h), chain_length(_chain), gpio(_pinmap), driver(_drv), i2sspeed(_i2sspeed), double_buff(_dbuff), - latch_blanking(_latblk) {} + latch_blanking(_latblk), + clkphase(_clockphase) {} }; // end of structure HUB75_I2S_CFG diff --git a/esp32_i2s_parallel_v2.c b/esp32_i2s_parallel_v2.c index ba88f10..966aaee 100644 --- a/esp32_i2s_parallel_v2.c +++ b/esp32_i2s_parallel_v2.c @@ -291,7 +291,10 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co for(int i = 0; i < bus_width; i++) { iomux_set_signal(conf->gpio_bus[i], iomux_signal_base + i); } - iomux_set_signal(conf->gpio_clk, iomux_clock); + iomux_set_signal(conf->gpio_clk, iomux_clock); + // invert clock phase if required + if (conf->clkphase) + GPIO.func_out_sel_cfg[conf->gpio_clk].inv_sel = 1; return ESP_OK; } diff --git a/esp32_i2s_parallel_v2.h b/esp32_i2s_parallel_v2.h index 16b094d..d0c6749 100644 --- a/esp32_i2s_parallel_v2.h +++ b/esp32_i2s_parallel_v2.h @@ -41,6 +41,7 @@ typedef struct { lldesc_t * lldesc_a; int desccount_b; // only used with double buffering lldesc_t * lldesc_b; // only used with double buffering + bool clkphase; // Clock signal phase } i2s_parallel_config_t; static inline int i2s_parallel_get_memory_width(i2s_port_t port, i2s_parallel_cfg_bits_t width) { From 6cab840dfc452dec1ce7e77681835609cac41088 Mon Sep 17 00:00:00 2001 From: Emil Muratov Date: Fri, 19 Feb 2021 17:25:29 +0300 Subject: [PATCH 2/4] Moved driver-specific code to a separate file Signed-off-by: Emil Muratov --- ESP32-HUB75-MatrixPanel-I2S-DMA.cpp | 82 ---------------------- ESP32-HUB75-MatrixPanel-I2S-DMA.h | 9 ++- ESP32-HUB75-MatrixPanel-leddrivers.cpp | 94 ++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 84 deletions(-) create mode 100644 ESP32-HUB75-MatrixPanel-leddrivers.cpp diff --git a/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp b/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp index 2e08af0..fa02d6d 100644 --- a/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp +++ b/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp @@ -587,88 +587,6 @@ void MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint8_t red, uint8_t green, uint } // colour depth loop (8) } // updateMatrixDMABuffer (full frame paint) - -#define CLK_PULSE digitalWrite(_cfg.gpio.clk, HIGH); digitalWrite(_cfg.gpio.clk, LOW); - -/** - * pre-init procedures for specific drivers - * - */ -void MatrixPanel_I2S_DMA::shiftDriver(const HUB75_I2S_CFG& _cfg){ - switch (_cfg.driver){ - case HUB75_I2S_CFG::ICN2038S: - case HUB75_I2S_CFG::FM6124: - case HUB75_I2S_CFG::FM6126A: - { - #if SERIAL_DEBUG - Serial.println( F("MatrixPanel_I2S_DMA - initializing FM6124 driver...")); - #endif - bool REG1[16] = {0,0,0,0,0, 1,1,1,1,1,1, 0,0,0,0,0}; // this sets global matrix brightness power - bool REG2[16] = {0,0,0,0,0, 0,0,0,0,1,0, 0,0,0,0,0}; // a single bit enables the matrix output - - for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2, _cfg.gpio.clk, _cfg.gpio.lat, _cfg.gpio.oe}){ - pinMode(_pin, OUTPUT); - digitalWrite(_pin, LOW); - } - - digitalWrite(_cfg.gpio.oe, HIGH); // Disable Display - - // Send Data to control register REG1 - // this sets the matrix brightness actually - for (int l = 0; l < PIXELS_PER_ROW; l++){ - for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2}) - digitalWrite(_pin, REG1[l%16]); // we have 16 bits shifters and write the same value all over the matrix array - - if (l > PIXELS_PER_ROW - 12){ // pull the latch 11 clocks before the end of matrix so that REG1 starts counting to save the value - digitalWrite(_cfg.gpio.lat, HIGH); - } - CLK_PULSE - } - - // drop the latch and save data to the REG1 all over the FM6124 chips - digitalWrite(_cfg.gpio.lat, LOW); - - // Send Data to control register REG2 (enable LED output) - for (int l = 0; l < PIXELS_PER_ROW; l++){ - for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2}) - digitalWrite(_pin, REG2[l%16]); // we have 16 bits shifters and we write the same value all over the matrix array - - if (l > PIXELS_PER_ROW - 13){ // pull the latch 12 clocks before the end of matrix so that reg2 stars counting to save the value - digitalWrite(_cfg.gpio.lat, HIGH); - } - CLK_PULSE - } - - // drop the latch and save data to the REG1 all over the FM6126 chips - digitalWrite(_cfg.gpio.lat, LOW); - - // blank data regs to keep matrix clear after manipulations - for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2}) - digitalWrite(_pin, LOW); - - for (int l = 0; l < PIXELS_PER_ROW; ++l){ - CLK_PULSE - } - - digitalWrite(_cfg.gpio.lat, HIGH); - CLK_PULSE - digitalWrite(_cfg.gpio.lat, LOW); - digitalWrite(_cfg.gpio.oe, LOW); // Enable Display - CLK_PULSE - } - break; - case HUB75_I2S_CFG::MBI5124: - /* MBI5124 chips must be clocked with positive-edge, since it's LAT signal - * resets on clock's rising edge while high - * https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/files/5952216/5a542453754da.pdf - */ - m_cfg.clkphase=true; - case HUB75_I2S_CFG::SHIFT: - default: - break; - } -} - /** * clear screen to black and reset all service bits */ diff --git a/ESP32-HUB75-MatrixPanel-I2S-DMA.h b/ESP32-HUB75-MatrixPanel-I2S-DMA.h index 81cfd95..8075051 100644 --- a/ESP32-HUB75-MatrixPanel-I2S-DMA.h +++ b/ESP32-HUB75-MatrixPanel-I2S-DMA.h @@ -227,7 +227,7 @@ struct HUB75_I2S_CFG { * Enumeration of hardware-specific chips * used to drive matrix modules */ - enum shift_driver {SHIFT=0, FM6124, FM6126A, ICN2038S, MBI5124}; + enum shift_driver {SHIFTREG=0, FM6124, FM6126A, ICN2038S, MBI5124}; /** * I2S clock speed selector @@ -282,7 +282,7 @@ struct HUB75_I2S_CFG { R1_PIN_DEFAULT, G1_PIN_DEFAULT, B1_PIN_DEFAULT, R2_PIN_DEFAULT, G2_PIN_DEFAULT, B2_PIN_DEFAULT, A_PIN_DEFAULT, B_PIN_DEFAULT, C_PIN_DEFAULT, D_PIN_DEFAULT, E_PIN_DEFAULT, LAT_PIN_DEFAULT, OE_PIN_DEFAULT, CLK_PIN_DEFAULT }, - shift_driver _drv = SHIFT, + shift_driver _drv = SHIFTREG, bool _dbuff = false, clk_speed _i2sspeed = HZ_10M, uint16_t _latblk = 1, @@ -641,6 +641,11 @@ class MatrixPanel_I2S_DMA { */ void shiftDriver(const HUB75_I2S_CFG& opts); + /** + * @brief - FM6124-family chips initialization routine + */ + void fm6124init(const HUB75_I2S_CFG& _cfg); + /** * @brief - reset OE bits in DMA buffer in a way to control brightness * @param brt - brightness level from 0 to row_width diff --git a/ESP32-HUB75-MatrixPanel-leddrivers.cpp b/ESP32-HUB75-MatrixPanel-leddrivers.cpp new file mode 100644 index 0000000..bb482a2 --- /dev/null +++ b/ESP32-HUB75-MatrixPanel-leddrivers.cpp @@ -0,0 +1,94 @@ +/* + Various LED Driver chips might need some specific code for initialisation/control logic + +*/ + +#include +#include "ESP32-HUB75-MatrixPanel-I2S-DMA.h" + +#define CLK_PULSE digitalWrite(_cfg.gpio.clk, HIGH); digitalWrite(_cfg.gpio.clk, LOW); + +/** + * @brief - pre-init procedures for specific led-drivers + * this method is called before DMA/I2S setup while GPIOs + * aint yet assigned for DMA operation + * + */ +void MatrixPanel_I2S_DMA::shiftDriver(const HUB75_I2S_CFG& _cfg){ + switch (_cfg.driver){ + case HUB75_I2S_CFG::ICN2038S: + case HUB75_I2S_CFG::FM6124: + case HUB75_I2S_CFG::FM6126A: + fm6124init(_cfg); + break; + case HUB75_I2S_CFG::MBI5124: + /* MBI5124 chips must be clocked with positive-edge, since it's LAT signal + * resets on clock's rising edge while high + * https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/files/5952216/5a542453754da.pdf + */ + m_cfg.clkphase=true; + break; + case HUB75_I2S_CFG::SHIFTREG: + default: + break; + } +} + + +void MatrixPanel_I2S_DMA::fm6124init(const HUB75_I2S_CFG& _cfg){ + #if SERIAL_DEBUG + Serial.println( F("MatrixPanel_I2S_DMA - initializing FM6124 driver...")); + #endif + bool REG1[16] = {0,0,0,0,0, 1,1,1,1,1,1, 0,0,0,0,0}; // this sets global matrix brightness power + bool REG2[16] = {0,0,0,0,0, 0,0,0,0,1,0, 0,0,0,0,0}; // a single bit enables the matrix output + + for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2, _cfg.gpio.clk, _cfg.gpio.lat, _cfg.gpio.oe}){ + pinMode(_pin, OUTPUT); + digitalWrite(_pin, LOW); + } + + digitalWrite(_cfg.gpio.oe, HIGH); // Disable Display + + // Send Data to control register REG1 + // this sets the matrix brightness actually + for (int l = 0; l < PIXELS_PER_ROW; l++){ + for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2}) + digitalWrite(_pin, REG1[l%16]); // we have 16 bits shifters and write the same value all over the matrix array + + if (l > PIXELS_PER_ROW - 12){ // pull the latch 11 clocks before the end of matrix so that REG1 starts counting to save the value + digitalWrite(_cfg.gpio.lat, HIGH); + } + CLK_PULSE + } + + // drop the latch and save data to the REG1 all over the FM6124 chips + digitalWrite(_cfg.gpio.lat, LOW); + + // Send Data to control register REG2 (enable LED output) + for (int l = 0; l < PIXELS_PER_ROW; l++){ + for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2}) + digitalWrite(_pin, REG2[l%16]); // we have 16 bits shifters and we write the same value all over the matrix array + + if (l > PIXELS_PER_ROW - 13){ // pull the latch 12 clocks before the end of matrix so that reg2 stars counting to save the value + digitalWrite(_cfg.gpio.lat, HIGH); + } + CLK_PULSE + } + + // drop the latch and save data to the REG1 all over the FM6126 chips + digitalWrite(_cfg.gpio.lat, LOW); + + // blank data regs to keep matrix clear after manipulations + for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2}) + digitalWrite(_pin, LOW); + + for (int l = 0; l < PIXELS_PER_ROW; ++l){ + CLK_PULSE + } + + digitalWrite(_cfg.gpio.lat, HIGH); + CLK_PULSE + digitalWrite(_cfg.gpio.lat, LOW); + digitalWrite(_cfg.gpio.oe, LOW); // Enable Display + CLK_PULSE +} \ No newline at end of file From f7aaad842b6200d79ce769f74a4d8c3957190582 Mon Sep 17 00:00:00 2001 From: mrfaptastic <12006953+mrfaptastic@users.noreply.github.com> Date: Sat, 20 Feb 2021 22:29:22 +0000 Subject: [PATCH 3/4] Add stopDMAtransfer function --- ESP32-HUB75-MatrixPanel-I2S-DMA.h | 11 ++++++++++- esp32_i2s_parallel_v2.c | 16 ++++++++++++++++ esp32_i2s_parallel_v2.h | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/ESP32-HUB75-MatrixPanel-I2S-DMA.h b/ESP32-HUB75-MatrixPanel-I2S-DMA.h index 8075051..dce549e 100644 --- a/ESP32-HUB75-MatrixPanel-I2S-DMA.h +++ b/ESP32-HUB75-MatrixPanel-I2S-DMA.h @@ -538,6 +538,16 @@ class MatrixPanel_I2S_DMA { * */ const HUB75_I2S_CFG& getCfg() const {return m_cfg;}; + + + /** + * Stop the ESP32 DMA Engine. Screen will forever be black until next ESP reboot. + */ + void stopDMAoutput() { + clearScreen(); + i2s_parallel_stop_dma(I2S_NUM_1); + } + // ------- PROTECTED ------- @@ -592,7 +602,6 @@ class MatrixPanel_I2S_DMA { void fillRectDMA(int16_t x_coord, int16_t y_coord, int16_t w, int16_t h, uint8_t r, uint8_t g, uint8_t b); #endif - // ------- PRIVATE ------- private: diff --git a/esp32_i2s_parallel_v2.c b/esp32_i2s_parallel_v2.c index 966aaee..cb298bf 100644 --- a/esp32_i2s_parallel_v2.c +++ b/esp32_i2s_parallel_v2.c @@ -299,6 +299,22 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co return ESP_OK; } + esp_err_t i2s_parallel_stop_dma(i2s_port_t port) { + if(port < I2S_NUM_0 || port >= I2S_NUM_MAX) { + return ESP_ERR_INVALID_ARG; + } + + i2s_dev_t* dev = I2S[port]; + + // Stop all ongoing DMA operations + dev->out_link.stop = 1; + dev->out_link.start = 0; + dev->conf.tx_start = 0; + + return ESP_OK; +} + + esp_err_t i2s_parallel_send_dma(i2s_port_t port, lldesc_t* dma_descriptor) { if(port < I2S_NUM_0 || port >= I2S_NUM_MAX) { return ESP_ERR_INVALID_ARG; diff --git a/esp32_i2s_parallel_v2.h b/esp32_i2s_parallel_v2.h index d0c6749..e344b9a 100644 --- a/esp32_i2s_parallel_v2.h +++ b/esp32_i2s_parallel_v2.h @@ -66,6 +66,7 @@ void link_dma_desc(volatile lldesc_t *dmadesc, volatile lldesc_t *prevdmadesc, v // I2S DMA Peripheral Setup Functions 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); // For frame buffer flipping / double buffering From 6f5991b9c32ea32361f107a6516ba98457d94350 Mon Sep 17 00:00:00 2001 From: mrfaptastic <12006953+mrfaptastic@users.noreply.github.com> Date: Sat, 20 Feb 2021 22:34:40 +0000 Subject: [PATCH 4/4] Update esp32_i2s_parallel_v2.c --- esp32_i2s_parallel_v2.c | 1 - 1 file changed, 1 deletion(-) diff --git a/esp32_i2s_parallel_v2.c b/esp32_i2s_parallel_v2.c index cb298bf..9aa6b9c 100644 --- a/esp32_i2s_parallel_v2.c +++ b/esp32_i2s_parallel_v2.c @@ -37,7 +37,6 @@ void setShiftCompleteCallback(callback f) { volatile bool previousBufferFree = true; static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) - // REG_WRITE(I2S_INT_CLR_REG(1), (REG_READ(I2S_INT_RAW_REG(1)) & 0xffffffc0) | 0x3f); if ( (*(i2s_port_t*)arg) == I2S_NUM_1 ) { // https://www.bogotobogo.com/cplusplus/pointers2_voidpointers_arrays.php //For I2S1