Compare commits

...

1 commit

Author SHA1 Message Date
95dccdcc38
Fix: allow 80MHz clock speed 2024-08-20 13:12:32 +02:00
2 changed files with 560 additions and 561 deletions

View file

@ -306,7 +306,7 @@ struct HUB75_I2S_CFG
bool clkphase; bool clkphase;
// Minimum refresh / scan rate needs to be configured on start due to LSBMSB_TRANSITION_BIT calculation in allocateDMAmemory() // Minimum refresh / scan rate needs to be configured on start due to LSBMSB_TRANSITION_BIT calculation in allocateDMAmemory()
uint8_t min_refresh_rate; uint16_t min_refresh_rate;
// struct constructor // struct constructor
HUB75_I2S_CFG( HUB75_I2S_CFG(
@ -498,7 +498,6 @@ public:
vlineDMA(x, y, h, r, g, b); vlineDMA(x, y, h, r, g, b);
else else
hlineDMA(x, y, w, r, g, b); hlineDMA(x, y, w, r, g, b);
} }
// rgb888 overload // rgb888 overload
virtual inline void drawFastVLine(int16_t x, int16_t y, int16_t h, uint8_t r, uint8_t g, uint8_t b) virtual inline void drawFastVLine(int16_t x, int16_t y, int16_t h, uint8_t r, uint8_t g, uint8_t b)
@ -526,7 +525,6 @@ public:
vlineDMA(x, y, h, r, g, b); vlineDMA(x, y, h, r, g, b);
else else
hlineDMA(x, y, w, r, g, b); hlineDMA(x, y, w, r, g, b);
} }
// rgb888 overload // rgb888 overload
virtual inline void drawFastHLine(int16_t x, int16_t y, int16_t w, uint8_t r, uint8_t g, uint8_t b) virtual inline void drawFastHLine(int16_t x, int16_t y, int16_t w, uint8_t r, uint8_t g, uint8_t b)
@ -550,7 +548,6 @@ public:
transform(x, y, w, h); transform(x, y, w, h);
fillRectDMA(x, y, w, h, r, g, b); fillRectDMA(x, y, w, h, r, g, b);
} }
// rgb888 overload // 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) 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)
@ -558,7 +555,6 @@ public:
transform(x, y, w, h); transform(x, y, w, h);
fillRectDMA(x, y, w, h, r, g, b); fillRectDMA(x, y, w, h, r, g, b);
} }
#endif #endif
@ -606,9 +602,6 @@ public:
// back_buffer_id ^= 1; // back_buffer_id ^= 1;
back_buffer_id = back_buffer_id ^ 1; back_buffer_id = back_buffer_id ^ 1;
fb = &frame_buffer[back_buffer_id]; fb = &frame_buffer[back_buffer_id];
} }
/** /**
@ -716,11 +709,11 @@ protected:
clearFrameBuffer(0); clearFrameBuffer(0);
brtCtrlOEv2(brightness, 0); brtCtrlOEv2(brightness, 0);
if (m_cfg.double_buff) { if (m_cfg.double_buff)
{
clearFrameBuffer(1); clearFrameBuffer(1);
brtCtrlOEv2(brightness, 1); brtCtrlOEv2(brightness, 1);
} }
} }
@ -841,7 +834,6 @@ protected:
Bus_Parallel16 dma_bus; Bus_Parallel16 dma_bus;
private: private:
// Matrix i2s settings // Matrix i2s settings
HUB75_I2S_CFG m_cfg; HUB75_I2S_CFG m_cfg;

View file

@ -39,7 +39,6 @@ Modified heavily for the ESP32 HUB75 DMA library by:
#include <soc/gpio_sig_map.h> #include <soc/gpio_sig_map.h>
#include <soc/i2s_periph.h> //includes struct and reg #include <soc/i2s_periph.h> //includes struct and reg
#if defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP32)
#include <Arduino.h> #include <Arduino.h>
#endif #endif
@ -50,10 +49,10 @@ Modified heavily for the ESP32 HUB75 DMA library by:
// Get CPU freq function. // Get CPU freq function.
#include <soc/rtc.h> #include <soc/rtc.h>
volatile bool previousBufferFree = true; volatile bool previousBufferFree = true;
static void IRAM_ATTR i2s_isr(void* arg) { static void IRAM_ATTR i2s_isr(void *arg)
{
// From original Sprite_TM Code // From original Sprite_TM Code
// REG_WRITE(I2S_INT_CLR_REG(1), (REG_READ(I2S_INT_RAW_REG(1)) & 0xffffffc0) | 0x3f); // REG_WRITE(I2S_INT_CLR_REG(1), (REG_READ(I2S_INT_RAW_REG(1)) & 0xffffffc0) | 0x3f);
@ -65,11 +64,11 @@ Modified heavily for the ESP32 HUB75 DMA library by:
previousBufferFree = true; previousBufferFree = true;
} }
bool DRAM_ATTR i2s_parallel_is_previous_buffer_free() { bool DRAM_ATTR i2s_parallel_is_previous_buffer_free()
{
return previousBufferFree; return previousBufferFree;
} }
// Static // Static
i2s_dev_t *getDev() i2s_dev_t *getDev()
{ {
@ -78,7 +77,6 @@ Modified heavily for the ESP32 HUB75 DMA library by:
#else #else
return (ESP32_I2S_DEVICE == 0) ? &I2S0 : &I2S1; return (ESP32_I2S_DEVICE == 0) ? &I2S0 : &I2S1;
#endif #endif
} }
// Static // Static
@ -93,8 +91,10 @@ Modified heavily for the ESP32 HUB75 DMA library by:
} }
} }
inline int i2s_parallel_get_memory_width(int port, int width) { inline int i2s_parallel_get_memory_width(int port, int width)
switch(width) { {
switch (width)
{
case 8: case 8:
#if !defined(CONFIG_IDF_TARGET_ESP32S2) #if !defined(CONFIG_IDF_TARGET_ESP32S2)
@ -103,7 +103,9 @@ Modified heavily for the ESP32 HUB75 DMA library by:
if (port == 1) if (port == 1)
{ {
return 1; return 1;
} else { }
else
{
return 2; return 2;
} }
#else #else
@ -119,7 +121,6 @@ Modified heavily for the ESP32 HUB75 DMA library by:
} }
} }
void Bus_Parallel16::config(const config_t &cfg) void Bus_Parallel16::config(const config_t &cfg)
{ {
ESP_LOGI("ESP32/S2", "Performing config for ESP32 or ESP32-S2"); ESP_LOGI("ESP32/S2", "Performing config for ESP32 or ESP32-S2");
@ -131,7 +132,8 @@ Modified heavily for the ESP32 HUB75 DMA library by:
{ {
ESP_LOGI("ESP32/S2", "Performing DMA bus init() for ESP32 or ESP32-S2"); ESP_LOGI("ESP32/S2", "Performing DMA bus init() for ESP32 or ESP32-S2");
if(_cfg.parallel_width < 8 || _cfg.parallel_width >= 24) { if (_cfg.parallel_width < 8 || _cfg.parallel_width >= 24)
{
return false; return false;
} }
@ -149,7 +151,8 @@ Modified heavily for the ESP32 HUB75 DMA library by:
iomux_clock = I2S0O_WS_OUT_IDX; iomux_clock = I2S0O_WS_OUT_IDX;
irq_source = ETS_I2S0_INTR_SOURCE; irq_source = ETS_I2S0_INTR_SOURCE;
switch(_cfg.parallel_width) { switch (_cfg.parallel_width)
{
case 8: case 8:
case 16: case 16:
iomux_signal_base = I2S0O_DATA_OUT8_IDX; iomux_signal_base = I2S0O_DATA_OUT8_IDX;
@ -164,13 +167,15 @@ Modified heavily for the ESP32 HUB75 DMA library by:
#if !defined(CONFIG_IDF_TARGET_ESP32S2) #if !defined(CONFIG_IDF_TARGET_ESP32S2)
// Can't compile if I2S1 if it doesn't exist with that hardware's IDF.... // Can't compile if I2S1 if it doesn't exist with that hardware's IDF....
else { else
{
periph_module_reset(PERIPH_I2S1_MODULE); periph_module_reset(PERIPH_I2S1_MODULE);
periph_module_enable(PERIPH_I2S1_MODULE); periph_module_enable(PERIPH_I2S1_MODULE);
iomux_clock = I2S1O_WS_OUT_IDX; iomux_clock = I2S1O_WS_OUT_IDX;
irq_source = ETS_I2S1_INTR_SOURCE; irq_source = ETS_I2S1_INTR_SOURCE;
switch(_cfg.parallel_width) { switch (_cfg.parallel_width)
{
case 16: case 16:
iomux_signal_base = I2S1O_DATA_OUT8_IDX; iomux_signal_base = I2S1O_DATA_OUT8_IDX;
break; break;
@ -201,9 +206,11 @@ Modified heavily for the ESP32 HUB75 DMA library by:
// Route clock signal to clock pin // Route clock signal to clock pin
gpio_matrix_out(_cfg.pin_wr, iomux_clock, _cfg.invert_pclk, 0); // inverst clock if required gpio_matrix_out(_cfg.pin_wr, iomux_clock, _cfg.invert_pclk, 0); // inverst clock if required
for (size_t i = 0; i < bus_width; i++) { for (size_t i = 0; i < bus_width; i++)
{
if (pins[i] >= 0) { if (pins[i] >= 0)
{
gpio_matrix_out(pins[i], iomux_signal_base + i, false, false); gpio_matrix_out(pins[i], iomux_signal_base + i, false, false);
} }
} }
@ -227,12 +234,12 @@ Modified heavily for the ESP32 HUB75 DMA library by:
// auto _div_num = std::min(255u, 1 + ((pll_160M_clock_d2) / (1 + freq))); // 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 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) { if (_div_num < 2 || _div_num > 0xFF)
{
// return ESP_ERR_INVALID_ARG; // return ESP_ERR_INVALID_ARG;
_div_num = 8; _div_num = 8;
} }
ESP_LOGD("ESP32", "i2s pll_160M_clock_d2 clkm_div_num is: %u", _div_num); ESP_LOGD("ESP32", "i2s pll_160M_clock_d2 clkm_div_num is: %u", _div_num);
// I2S_CLK_SEL Set this bit to select I2S module clock source. // I2S_CLK_SEL Set this bit to select I2S module clock source.
@ -261,11 +268,14 @@ Modified heavily for the ESP32 HUB75 DMA library by:
// auto _div_num = std::min(255u, 1 + ((pll_d2_clock) / (1 + freq))); // 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 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) { if (_div_num < 2 || _div_num > 0xFF)
{
// return ESP_ERR_INVALID_ARG; // return ESP_ERR_INVALID_ARG;
_div_num = 4; _div_num = 4;
} }
_div_num = 1;
/// auto _div_num = 80000000L/freq; /// auto _div_num = 80000000L/freq;
ESP_LOGD("ESP32", "i2s pll_d2_clock clkm_div_num is: %u", _div_num); ESP_LOGD("ESP32", "i2s pll_d2_clock clkm_div_num is: %u", _div_num);
@ -279,11 +289,9 @@ Modified heavily for the ESP32 HUB75 DMA library by:
#endif #endif
output_freq = output_freq + 0; // work around arudino 'unused var' issue if debug isn't enabled. 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))); 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 // Setup i2s clock
dev->sample_rate_conf.val = 0; dev->sample_rate_conf.val = 0;
@ -327,10 +335,13 @@ Modified heavily for the ESP32 HUB75 DMA library by:
// Not really described for non-pcm modes, although datasheet states it should be set correctly even for LCD mode // Not really described for non-pcm modes, although datasheet states it should be set correctly even for LCD mode
// First stage config. Configures how data is loaded into fifo // First stage config. Configures how data is loaded into fifo
if(_cfg.parallel_width == 24) { if (_cfg.parallel_width == 24)
{
// Mode 0, single 32-bit channel, linear 32 bit load to fifo // Mode 0, single 32-bit channel, linear 32 bit load to fifo
dev->fifo_conf.tx_fifo_mod = 3; dev->fifo_conf.tx_fifo_mod = 3;
} else { }
else
{
// Mode 1, single 16-bit channel, load 16 bit sample(*) into fifo and pad to 32 bit with zeros // Mode 1, single 16-bit channel, load 16 bit sample(*) into fifo and pad to 32 bit with zeros
// *Actually a 32 bit read where two samples are read at once. Length of fifo must thus still be word-aligned // *Actually a 32 bit read where two samples are read at once. Length of fifo must thus still be word-aligned
dev->fifo_conf.tx_fifo_mod = 1; dev->fifo_conf.tx_fifo_mod = 1;
@ -353,18 +364,19 @@ Modified heavily for the ESP32 HUB75 DMA library by:
dev->conf.rx_fifo_reset = 1; dev->conf.rx_fifo_reset = 1;
#if defined(CONFIG_IDF_TARGET_ESP32S2) #if defined(CONFIG_IDF_TARGET_ESP32S2)
while(dev->conf.rx_fifo_reset_st); // esp32-s2 only while (dev->conf.rx_fifo_reset_st)
; // esp32-s2 only
#endif #endif
dev->conf.rx_fifo_reset = 0; dev->conf.rx_fifo_reset = 0;
dev->conf.tx_fifo_reset = 1; dev->conf.tx_fifo_reset = 1;
#if defined(CONFIG_IDF_TARGET_ESP32S2) #if defined(CONFIG_IDF_TARGET_ESP32S2)
while(dev->conf.tx_fifo_reset_st); // esp32-s2 only while (dev->conf.tx_fifo_reset_st)
; // esp32-s2 only
#endif #endif
dev->conf.tx_fifo_reset = 0; dev->conf.tx_fifo_reset = 0;
// Reset DMA // Reset DMA
dev->lc_conf.in_rst = 1; dev->lc_conf.in_rst = 1;
dev->lc_conf.in_rst = 0; dev->lc_conf.in_rst = 0;
@ -377,7 +389,6 @@ Modified heavily for the ESP32 HUB75 DMA library by:
dev->in_link.val = 0; dev->in_link.val = 0;
dev->out_link.val = 0; dev->out_link.val = 0;
// Device reset // Device reset
dev->conf.rx_reset = 1; dev->conf.rx_reset = 1;
dev->conf.tx_reset = 1; dev->conf.tx_reset = 1;
@ -388,7 +399,6 @@ Modified heavily for the ESP32 HUB75 DMA library by:
dev->conf1.tx_stop_en = 0; dev->conf1.tx_stop_en = 0;
dev->timing.val = 0; dev->timing.val = 0;
// If we have double buffering, then allocate an interrupt service routine function // If we have double buffering, then allocate an interrupt service routine function
// that can be used for I2S0/I2S1 created interrupts. // that can be used for I2S0/I2S1 created interrupts.
@ -398,7 +408,6 @@ Modified heavily for the ESP32 HUB75 DMA library by:
// Allocate a level 1 intterupt: lowest priority, as ISR isn't urgent and may take a long time to complete // Allocate a level 1 intterupt: lowest priority, as ISR isn't urgent and may take a long time to complete
esp_intr_alloc(irq_source, (int)(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1), i2s_isr, NULL, NULL); esp_intr_alloc(irq_source, (int)(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1), i2s_isr, NULL, NULL);
#if defined(CONFIG_IDF_TARGET_ESP32S2) #if defined(CONFIG_IDF_TARGET_ESP32S2)
ESP_LOGD("ESP32-S2", "init() GPIO and clock configuration set for ESP32-S2"); ESP_LOGD("ESP32-S2", "init() GPIO and clock configuration set for ESP32-S2");
#else #else
@ -408,7 +417,6 @@ Modified heavily for the ESP32 HUB75 DMA library by:
return true; return true;
} }
void Bus_Parallel16::release(void) void Bus_Parallel16::release(void)
{ {
if (_dmadesc_a) if (_dmadesc_a)
@ -434,7 +442,8 @@ Modified heavily for the ESP32 HUB75 DMA library by:
// Need this to work for double buffers etc. // Need this to work for double buffers etc.
bool Bus_Parallel16::allocate_dma_desc_memory(size_t len) bool Bus_Parallel16::allocate_dma_desc_memory(size_t len)
{ {
if (_dmadesc_a) heap_caps_free(_dmadesc_a); // free all dma descrptios previously if (_dmadesc_a)
heap_caps_free(_dmadesc_a); // free all dma descrptios previously
_dmadesc_count = len; _dmadesc_count = len;
_dmadesc_last = len - 1; _dmadesc_last = len - 1;
@ -449,10 +458,10 @@ Modified heavily for the ESP32 HUB75 DMA library by:
return false; return false;
} }
if (_double_dma_buffer) if (_double_dma_buffer)
{ {
if (_dmadesc_b) heap_caps_free(_dmadesc_b); // free all dma descrptios previously if (_dmadesc_b)
heap_caps_free(_dmadesc_b); // free all dma descrptios previously
ESP_LOGD("ESP32/S2", "Allocating the second buffer (double buffer enabled)."); ESP_LOGD("ESP32/S2", "Allocating the second buffer (double buffer enabled).");
@ -483,7 +492,6 @@ Modified heavily for the ESP32 HUB75 DMA library by:
_dmadesc_blank->offset = 0; _dmadesc_blank->offset = 0;
return true; return true;
} }
void Bus_Parallel16::create_dma_desc_link(void *data, size_t size, bool dmadesc_b) void Bus_Parallel16::create_dma_desc_link(void *data, size_t size, bool dmadesc_b)
@ -498,7 +506,8 @@ Modified heavily for the ESP32 HUB75 DMA library by:
if (!dmadesc_b) if (!dmadesc_b)
{ {
if ( (_dmadesc_a_idx+1) > _dmadesc_count) { if ((_dmadesc_a_idx + 1) > _dmadesc_count)
{
ESP_LOGE("ESP32/S2", "Attempted to create more DMA descriptors than allocated memory for. Expecting a maximum of %u DMA descriptors", (unsigned int)_dmadesc_count); ESP_LOGE("ESP32/S2", "Attempted to create more DMA descriptors than allocated memory for. Expecting a maximum of %u DMA descriptors", (unsigned int)_dmadesc_count);
return; return;
} }
@ -524,7 +533,8 @@ Modified heavily for the ESP32 HUB75 DMA library by:
eof = (_dmadesc_a_idx == (_dmadesc_last)); eof = (_dmadesc_a_idx == (_dmadesc_last));
} }
if ( _dmadesc_a_idx == (_dmadesc_last) ) { if (_dmadesc_a_idx == (_dmadesc_last))
{
ESP_LOGW("ESP32/S2", "Creating final DMA descriptor and linking back to 0."); ESP_LOGW("ESP32/S2", "Creating final DMA descriptor and linking back to 0.");
} }
@ -537,9 +547,12 @@ Modified heavily for the ESP32 HUB75 DMA library by:
dmadesc->qe.stqe_next = (lldesc_t *)next; dmadesc->qe.stqe_next = (lldesc_t *)next;
dmadesc->offset = 0; dmadesc->offset = 0;
if ( (dmadesc_b == true) ) { // for primary buffer if ((dmadesc_b == true))
{ // for primary buffer
_dmadesc_b_idx++; _dmadesc_b_idx++;
} else { }
else
{
_dmadesc_a_idx++; _dmadesc_a_idx++;
} }
@ -561,10 +574,8 @@ Modified heavily for the ESP32 HUB75 DMA library by:
dev->conf.tx_start = 1; dev->conf.tx_start = 1;
} // end } // end
void Bus_Parallel16::dma_transfer_stop() void Bus_Parallel16::dma_transfer_stop()
{ {
auto dev = _dev; auto dev = _dev;
@ -576,37 +587,33 @@ Modified heavily for the ESP32 HUB75 DMA library by:
} // end } // end
void Bus_Parallel16::flip_dma_output_buffer(int buffer_id) // pass by reference so we can change in main matrixpanel class void Bus_Parallel16::flip_dma_output_buffer(int buffer_id) // pass by reference so we can change in main matrixpanel class
{ {
// Setup interrupt handler which is focussed only on the (page 322 of Tech. Ref. Manual) // 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) // "I2S_OUT_EOF_INT: Triggered when rxlink has finished sending a packet" (when dma linked list with eof = 1 is hit)
if ( buffer_id == 1) { if (buffer_id == 1)
{
_dmadesc_a[_dmadesc_last].qe.stqe_next = &_dmadesc_b[0]; // Start sending out _dmadesc_b (or buffer 1) _dmadesc_a[_dmadesc_last].qe.stqe_next = &_dmadesc_b[0]; // Start sending out _dmadesc_b (or buffer 1)
// fix _dmadesc_ loop issue #407 // fix _dmadesc_ loop issue #407
// need to connect the up comming _dmadesc_ not the old one // need to connect the up comming _dmadesc_ not the old one
_dmadesc_b[_dmadesc_last].qe.stqe_next = &_dmadesc_b[0]; _dmadesc_b[_dmadesc_last].qe.stqe_next = &_dmadesc_b[0];
}
} else { else
{
_dmadesc_b[_dmadesc_last].qe.stqe_next = &_dmadesc_a[0]; _dmadesc_b[_dmadesc_last].qe.stqe_next = &_dmadesc_a[0];
_dmadesc_a[_dmadesc_last].qe.stqe_next = &_dmadesc_a[0]; _dmadesc_a[_dmadesc_last].qe.stqe_next = &_dmadesc_a[0];
} }
previousBufferFree = false; previousBufferFree = false;
// while (i2s_parallel_is_previous_buffer_free() == false) {} // while (i2s_parallel_is_previous_buffer_free() == false) {}
while (!previousBufferFree); while (!previousBufferFree)
;
} // end flip } // end flip
#endif #endif