Skip to content

Commit

Permalink
clock speed fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mrcodetastic committed Jul 17, 2024
1 parent e8d92c3 commit cee3dca
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 57 deletions.
16 changes: 11 additions & 5 deletions src/ESP32-HUB75-MatrixPanel-I2S-DMA.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,10 @@ struct HUB75_I2S_CFG
enum clk_speed
{
HZ_8M = 8000000,
HZ_10M = 10000000,
HZ_15M = 15000000,
HZ_20M = 20000000
HZ_10M = 8000000,
HZ_15M = 16000000, // for compatability
HZ_16M = 16000000,
HZ_20M = 16000000 // for compatability

This comment has been minimized.

Copy link
@Lukaswnd

Lukaswnd Jul 18, 2024

Contributor

Why? some panels are able to achive the 20mhz, why not set the default low, so most people won't complain and still enable higher frequencies?

};

//
Expand Down Expand Up @@ -318,9 +319,14 @@ 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 = SHIFTREG, bool _dbuff = false, clk_speed _i2sspeed = HZ_15M,
shift_driver _drv = SHIFTREG,
bool _dbuff = false,
clk_speed _i2sspeed = HZ_8M,
uint8_t _latblk = DEFAULT_LAT_BLANKING, // Anything > 1 seems to cause artefacts on ICS panels
bool _clockphase = true, uint16_t _min_refresh_rate = 60, uint8_t _pixel_color_depth_bits = PIXEL_COLOR_DEPTH_BITS_DEFAULT) : mx_width(_w), mx_height(_h), chain_length(_chain), gpio(_pinmap), driver(_drv), double_buff(_dbuff), i2sspeed(_i2sspeed), latch_blanking(_latblk), clkphase(_clockphase), min_refresh_rate(_min_refresh_rate)
bool _clockphase = true,
uint16_t _min_refresh_rate = 60,
uint8_t _pixel_color_depth_bits = PIXEL_COLOR_DEPTH_BITS_DEFAULT)
: mx_width(_w), mx_height(_h), chain_length(_chain), gpio(_pinmap), driver(_drv), double_buff(_dbuff), i2sspeed(_i2sspeed), latch_blanking(_latblk), clkphase(_clockphase), min_refresh_rate(_min_refresh_rate)
{
setPixelColorDepthBits(_pixel_color_depth_bits);
}
Expand Down
116 changes: 64 additions & 52 deletions src/platforms/esp32/esp32_i2s_parallel_dma.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ Modified heavily for the ESP32 HUB75 DMA library by:
#include <esp_err.h>
#include <esp_log.h>

// Get current frequecny
#include "esp_clk_tree.h"

// Get CPU freq function.
#include <soc/rtc.h>

Expand Down Expand Up @@ -131,9 +134,16 @@ Modified heavily for the ESP32 HUB75 DMA library by:
{
ESP_LOGI("ESP32/S2", "Performing DMA bus init() for ESP32 or ESP32-S2");

/*
if(_cfg.parallel_width < 8 || _cfg.parallel_width >= 24) {
return false;
}
*/

// Only 16 bit mode used for this library
if(_cfg.parallel_width != 16) {
return false;
}

auto dev = _dev;
volatile int iomux_signal_base;
Expand Down Expand Up @@ -209,46 +219,60 @@ Modified heavily for the ESP32 HUB75 DMA library by:
}

////////////////////////////// Clock configuration //////////////////////////////
unsigned int freq = (_cfg.bus_freq); // requested freq

// Setup i2s clock (sample rate)
dev->sample_rate_conf.val = 0;

// Third stage config, width of data to be written to IO (I think this should always be the actual data width?)
dev->sample_rate_conf.rx_bits_mod = bus_width;
dev->sample_rate_conf.tx_bits_mod = bus_width;

// Hard-code clock divider for ESP32-S2 to 8Mhz
#if defined (CONFIG_IDF_TARGET_ESP32S2)

unsigned int freq = (_cfg.bus_freq);
ESP_LOGD("ESP32/S2", "Requested output clock frequency: %u Mhz", (unsigned int)(freq/1000000));

// What is the current CPU frequency?
// I2S_CLK_SEL Set this bit to select I2S module clock source.
// 0: No clock. 1: APLL_CLK. 2: PLL_160M_CLK. 3: No clock. (R/W)
dev->clkm_conf.clk_sel = 2; // 160 Mhz

dev->clkm_conf.clkm_div_a = 1; // Clock denominator
dev->clkm_conf.clkm_div_b = 0; // Clock numerator

// Calculate clock divider for ESP32-S2
#if defined (CONFIG_IDF_TARGET_ESP32S2)

// Right shift (>> 1) and divide 160mhz in half to 80Mhz for the calc due to the fact
// that later we must have tx_bck_div_num = 2 for both esp32 and esp32-s2
dev->clkm_conf.clkm_div_num = 5; // 160 / 5 = 32 Mhz ('fi2s')
dev->clkm_conf.clk_en = 1;

//static uint32_t pll_160M_clock_d2 = 160 * 1000 * 1000 >> 1;
// Binary clock
/*
In LCD mode, WS clock frequency is:
fWS = fi2s / W ∗ 2
// I2S_CLKM_DIV_NUM 2=40MHz / 3=27MHz / 4=20MHz / 5=16MHz / 8=10MHz / 10=8MHz
//auto _div_num = std::min(255u, 1 + ((pll_160M_clock_d2) / (1 + freq)));
unsigned int _div_num = (unsigned int) (160000000L / freq / i2s_parallel_get_memory_width(ESP32_I2S_DEVICE, 16)); // 16 bits in parallel

if(_div_num < 2 || _div_num > 0xFF) {
// return ESP_ERR_INVALID_ARG;
_div_num = 8;
}


ESP_LOGD("ESP32", "i2s pll_160M_clock_d2 clkm_div_num is: %u", _div_num);
W is an integer in the range 1 to 64. W corresponds to the value of I2S_TX_BCK_DIV_NUM[5:0] in register
I2S_SAMPLE_RATE_CONF_REG as follows.
• When I2S_TX_BCK_DIV_NUM[5:0] = 0, W = 64.
• When I2S_TX_BCK_DIV_NUM[5:0] is set to other values, W = I2S_TX_BCK_DIV_NUM[5:0
*/

dev->sample_rate_conf.rx_bck_div_num = 2;

// ESP32 and ESP32-S2 TRM clearly say that "Note that I2S_TX_BCK_DIV_NUM[5:0] must not be configured as 1."
// Testing has revealed that setting it to 1 causes problems.
dev->sample_rate_conf.tx_bck_div_num = 2;

// Output Frequency is now
//160 / 5 / (2*2) = 8Mhz

// I2S_CLK_SEL Set this bit to select I2S module clock source.
// 0: No clock. 1: APLL_CLK. 2: PLL_160M_CLK. 3: No clock. (R/W)
dev->clkm_conf.clk_sel = 2;
dev->clkm_conf.clkm_div_a = 1; // Clock denominator
dev->clkm_conf.clkm_div_b = 0; // Clock numerator
dev->clkm_conf.clkm_div_num = _div_num;
dev->clkm_conf.clk_en = 1;

// Calc
unsigned int output_freq = (unsigned int)(160000000L/_div_num);

// Calculate clock divider for Original ESP32
#else

dev->sample_rate_conf.rx_bck_div_num = 1;

// ESP32 and ESP32-S2 TRM clearly say that "Note that I2S_TX_BCK_DIV_NUM[5:0] must not be configured as 1."
// Testing has revealed that setting it to 1 causes problems.
dev->sample_rate_conf.tx_bck_div_num = 1;


// Note: clkm_div_num must only be set here AFTER clkm_div_b, clkm_div_a, etc. Or weird things happen!
// On original ESP32, max I2S DMA parallel speed is 20Mhz.

Expand All @@ -260,42 +284,30 @@ Modified heavily for the ESP32 HUB75 DMA library by:
// I2S_CLKM_DIV_NUM 2=40MHz / 3=27MHz / 4=20MHz / 5=16MHz / 8=10MHz / 10=8MHz
//auto _div_num = std::min(255u, 1 + ((pll_d2_clock) / (1 + freq)));

/*
unsigned int _div_num = (unsigned int) (80000000L / freq / i2s_parallel_get_memory_width(ESP32_I2S_DEVICE, 16)); // 16 bits in parallel
if(_div_num < 2 || _div_num > 0xFF) {
// return ESP_ERR_INVALID_ARG;
_div_num = 4;
}

///auto _div_num = 80000000L/freq;
*/

unsigned int _div_num = (freq > 8000000) ? 5:10; // 8 mhz or 16mhz

ESP_LOGD("ESP32", "i2s pll_d2_clock clkm_div_num is: %u", _div_num);

dev->clkm_conf.clka_en=1; // Use the 80mhz system clock (PLL_D2_CLK) when '0'
dev->clkm_conf.clka_en=0; // Use the 80mhz system clock (PLL_D2_CLK) when '0'
dev->clkm_conf.clkm_div_a = 1; // Clock denominator
dev->clkm_conf.clkm_div_b = 0; // Clock numerator
dev->clkm_conf.clkm_div_num = _div_num;

unsigned int output_freq = (unsigned int)(80000000L/_div_num);
// dev->clkm_conf.clk_en=1;

// Note tx_bck_div_num value of 2 will further divide clock rate

#endif


output_freq = output_freq + 0; // work around arudino 'unused var' issue if debug isn't enabled.
ESP_LOGI("ESP32/S2", "Output frequency is %u Mhz??", (unsigned int)(output_freq/1000000/i2s_parallel_get_memory_width(ESP32_I2S_DEVICE, 16)));


// Setup i2s clock
dev->sample_rate_conf.val = 0;

// Third stage config, width of data to be written to IO (I think this should always be the actual data width?)
dev->sample_rate_conf.rx_bits_mod = bus_width;
dev->sample_rate_conf.tx_bits_mod = bus_width;

// Serial clock
// ESP32 and ESP32-S2 TRM clearly say that "Note that I2S_TX_BCK_DIV_NUM[5:0] must not be configured as 1."
dev->sample_rate_conf.rx_bck_div_num = 2;
dev->sample_rate_conf.tx_bck_div_num = 2;


////////////////////////////// END CLOCK CONFIGURATION /////////////////////////////////

// I2S conf2 reg
Expand Down
20 changes: 20 additions & 0 deletions src/platforms/esp32s3/gdma_lcd_parallel16.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@
return true;
}

// LCD end of transaction interrupt
static void IRAM_ATTR lcd_isr(void* arg) {

LCD_CAM.lc_dma_int_clr.lcd_trans_done_int_clr = 1;

previousBufferFree = true;

}


lcd_cam_dev_t* getDev()
{
return &LCD_CAM;
Expand Down Expand Up @@ -259,11 +269,21 @@
gdma_set_transfer_ability(dma_chan, &ability);

// Enable DMA transfer callback
/*
static gdma_tx_event_callbacks_t tx_cbs = {
// .on_trans_eof is literally the only gdma tx event type available
.on_trans_eof = gdma_on_trans_eof_callback
};
gdma_register_tx_event_callbacks(dma_chan, &tx_cbs, NULL);
*/

//
// Enable Transaction Done interrupt
LCD_CAM.lc_dma_int_ena.lcd_trans_done_int_ena = 1;

// Allocate a level 1 intterupt: lowest priority, as ISR isn't urgent and may take a long time to complete
esp_intr_alloc(ETS_LCD_CAM_INTR_SOURCE, (int)(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1), lcd_isr, NULL, NULL);

This comment has been minimized.

Copy link
@Lukaswnd

Lukaswnd Jul 18, 2024

Contributor

This is not working for me. The lcd_isr never get's called.
I am using Arduino on a generic esp32s3r8n16



// This uses a busy loop to wait for each DMA transfer to complete...
Expand Down

0 comments on commit cee3dca

Please sign in to comment.