aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormrcodetastic <12006953+mrcodetastic@users.noreply.github.com>2025-02-24 01:38:50 +0000
committermrcodetastic <12006953+mrcodetastic@users.noreply.github.com>2025-02-24 01:38:50 +0000
commit33883656da5cdd8ddf8e4882344a6968e00be890 (patch)
tree774fa83a9bbb8972b4273c445979b5983cad7166 /src
parent67c4decce0457b38279827be964330921f293adc (diff)
#755
Diffstat (limited to 'src')
-rw-r--r--src/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp309
-rw-r--r--src/ESP32-HUB75-MatrixPanel-I2S-DMA.h78
2 files changed, 203 insertions, 184 deletions
diff --git a/src/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp b/src/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp
index 0ee3fa2..55761e7 100644
--- a/src/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp
+++ b/src/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp
@@ -13,7 +13,7 @@
// #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)])
// BufferID is now ignored, seperate global pointer pointer!
-#define getRowDataPtr(row, _dpth, buff_id) &(fb->rowBits[row]->data[_dpth * fb->rowBits[row]->width])
+#define getRowDataPtr(row, _dpth) &(fb->rowBits[row]->data[_dpth * fb->rowBits[row]->width])
/* 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
@@ -32,46 +32,53 @@
*/
#define PIXEL_COLOR_MASK_BIT(color_depth_index, mask_offset) (1 << (color_depth_index + mask_offset))
-bool MatrixPanel_I2S_DMA::allocateDMAmemory()
+bool MatrixPanel_I2S_DMA::setupDMA(const HUB75_I2S_CFG &_cfg)
{
-
+
+ /***
+ * Step 0: Allocate basic DMA framebuffer memory for the data we send out in parallel to the HUB75 panel.
+ * Colour depth is the only consideration.
+ *
+ */
ESP_LOGI("I2S-DMA", "Free heap: %d", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
ESP_LOGI("I2S-DMA", "Free SPIRAM: %d", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
- // Alright, theoretically we should be OK, so let us do this, so
- // lets allocate a chunk of memory for each row (a row could span multiple panels if chaining is in place)
- ESP_LOGI("I2S-DMA", "allocating rowBitStructs with pixel_color_depth_bits of %d", m_cfg.getPixelColorDepthBits());
- // iterate through number of rows, allocate memory for each
size_t allocated_fb_memory = 0;
int fbs_required = (m_cfg.double_buff) ? 2 : 1;
+
for (int fb = 0; fb < (fbs_required); fb++)
{
frame_buffer[fb].rowBits.reserve(ROWS_PER_FRAME);
for (int malloc_num = 0; malloc_num < ROWS_PER_FRAME; malloc_num++)
{
- auto ptr = std::make_shared<rowBitStruct>(PIXELS_PER_ROW, m_cfg.getPixelColorDepthBits(), m_cfg.double_buff);
+ auto ptr = std::make_shared<rowBitStruct>(PIXELS_PER_ROW, m_cfg.getPixelColorDepthBits());
- if (ptr->data == nullptr)
- {
- ESP_LOGE("I2S-DMA", "CRITICAL ERROR: Not enough memory for requested colour depth! Please reduce pixel_color_depth_bits value.\r\n");
- ESP_LOGE("I2S-DMA", "Could not allocate rowBitStruct %d!.\r\n", malloc_num);
+ if (ptr->data == nullptr) {
+
+ ESP_LOGE("I2S-DMA", "CRITICAL ERROR: Not enough memory for requested colour depth of %d bits! Please reduce pixel_color_depth_bits value.\r\n", m_cfg.getPixelColorDepthBits());
return false;
// TODO: should we release all previous rowBitStructs here???
}
- allocated_fb_memory += ptr->getColorDepthSize(); // byte required to display all colour depths for the rows shown at the same time
+ allocated_fb_memory += ptr->getColorDepthSize(false); // byte required to display all colour depths for the two parallel rows
frame_buffer[fb].rowBits.emplace_back(ptr); // save new rowBitStruct pointer into rows vector
++frame_buffer[fb].rows;
}
}
ESP_LOGI("I2S-DMA", "Allocating %d bytes memory for DMA BCM framebuffer(s).", allocated_fb_memory);
- // calculate the lowest LSBMSB_TRANSITION_BIT value that will fit in memory that will meet or exceed the configured refresh rate
-
-//#define FORCE_COLOR_DEPTH 1
+
+ /***
+ * Step 1: Check what the minimum refresh rate is, and calculate the lsbMsbTransitionBit
+ * which is the bit at which we can reduce the colour depth to achieve the minimum refresh rate.
+ *
+ * This also determines the number of DMA descriptors required per row.
+ */
+
+//#define FORCE_COLOR_DEPTH 1
#if !defined(FORCE_COLOR_DEPTH)
@@ -80,14 +87,16 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
while (1)
{
int psPerClock = 1000000000000UL / m_cfg.i2sspeed;
- int nsPerLatch = ((PIXELS_PER_ROW + CLKS_DURING_LATCH) * psPerClock) / 1000;
+ int nsPerLatch = ((PIXELS_PER_ROW + CLKS_DURING_LATCH) * psPerClock) / 1000; // time per row
// add time to shift out LSBs + LSB-MSB transition bit - this ignores fractions...
int nsPerRow = m_cfg.getPixelColorDepthBits() * nsPerLatch;
- // add time to shift out MSBs
- for (int i = lsbMsbTransitionBit + 1; i < m_cfg.getPixelColorDepthBits(); i++)
- nsPerRow += (1 << (i - lsbMsbTransitionBit - 1)) * (m_cfg.getPixelColorDepthBits() - i) * nsPerLatch;
+ // Now add the time for the remaining bit depths
+ for (int i = lsbMsbTransitionBit + 1; i < m_cfg.getPixelColorDepthBits(); i++) {
+ //nsPerRow += (1 << (i - lsbMsbTransitionBit - 1)) * (m_cfg.getPixelColorDepthBits() - i) * nsPerLatch;
+ nsPerRow += (1 << (i - lsbMsbTransitionBit - 1)) * nsPerLatch;
+ }
int nsPerFrame = nsPerRow * ROWS_PER_FRAME;
int actualRefreshRate = 1000000000UL / (nsPerFrame);
@@ -114,154 +123,148 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
#endif
/***
- * Step 2a: lsbMsbTransition bit is now finalised - recalculate the DMA descriptor count required, which is used for
- * memory allocation of the DMA linked list memory structure.
+ * Step 2: Calculate the DMA descriptors required, which is used for memory allocation of the DMA linked list memory structure.
+ * This determines the number of passes required to shift out the colour bits in the framebuffer.
+ * We need to also take into consderation where a chain of panels (pixels) is so long, it requires more than one DMA payload,
+ * give this library's DMA output memory allocation approach is by the row.
*/
- int numDMAdescriptorsPerRow = 1;
- for (int i = lsbMsbTransitionBit + 1; i < m_cfg.getPixelColorDepthBits(); i++)
- {
- numDMAdescriptorsPerRow += (1 << (i - lsbMsbTransitionBit - 1));
- }
-
- ESP_LOGI("I2S-DMA", "Recalculated number of DMA descriptors per row: %d", numDMAdescriptorsPerRow);
-
- // Refer to 'DMA_LL_PAYLOAD_SPLIT' code in configureDMA() below to understand why this exists.
- // numDMAdescriptorsPerRow is also used to calculate descount which is super important in i2s_parallel_config_t SoC DMA setup.
- if (frame_buffer[0].rowBits[0]->getColorDepthSize() > DMA_MAX)
- {
+
+ int dma_descs_per_row_1cdepth = (frame_buffer[0].rowBits[0]->getColorDepthSize(true) + DMA_MAX - 1 ) / DMA_MAX;
+ size_t last_dma_desc_bytes_1cdepth = (frame_buffer[0].rowBits[0]->getColorDepthSize(true) % DMA_MAX);
+
+ int dma_descs_per_row_all_cdepths = (frame_buffer[0].rowBits[0]->getColorDepthSize(false) + DMA_MAX - 1 ) / DMA_MAX;
+ size_t last_dma_desc_bytes_all_cdepths = (frame_buffer[0].rowBits[0]->getColorDepthSize(false) % DMA_MAX);
- ESP_LOGW("I2S-DMA", "rowBits struct is too large to fit in one DMA transfer payload, splitting required. Adding %d DMA descriptors\n", m_cfg.getPixelColorDepthBits() - 1);
+ // Logging the calculated values
+ ESP_LOGV("I2S-DMA", "dma_descs_per_row_1cdepth: %d", dma_descs_per_row_1cdepth);
+ ESP_LOGV("I2S-DMA", "last_dma_desc_bytes_1cdepth: %zu", last_dma_desc_bytes_1cdepth);
+ ESP_LOGV("I2S-DMA", "dma_descs_per_row_all_cdepths: %d", dma_descs_per_row_all_cdepths);
+ ESP_LOGV("I2S-DMA", "last_dma_desc_bytes_all_cdepths: %zu", last_dma_desc_bytes_all_cdepths);
+
+
+ // Calculate per-row number
+ int dma_descriptors_per_row = dma_descs_per_row_all_cdepths;
- numDMAdescriptorsPerRow += m_cfg.getPixelColorDepthBits() - 1;
- // Note: If numDMAdescriptorsPerRow is even just one descriptor too large, DMA linked list will not correctly loop.
+ // Add descriptors for MSB bits after transition
+ for (int i = lsbMsbTransitionBit + 1; i < m_cfg.getPixelColorDepthBits(); i++) {
+ dma_descriptors_per_row += (1 << (i - lsbMsbTransitionBit - 1)) * dma_descs_per_row_1cdepth;
}
+
+ //dma_descriptors_per_row = 1;
+
+ // Allocate DMA descriptors
+ int dma_descriptions_required = dma_descriptors_per_row * ROWS_PER_FRAME;
+
+ ESP_LOGV("I2S-DMA", "DMA descriptors per row: %d", dma_descriptors_per_row);
+ ESP_LOGV("I2S-DMA", "DMA descriptors required in total: %d", dma_descriptions_required);
/***
- * Step 3: Allocate memory for DMA linked list, linking up each framebuffer row in sequence for GPIO output.
+ * Step 3: Allocate the DMA descriptor memory via. the relevant platform DMA implementation class.
*/
- // malloc the DMA linked list descriptors that i2s_parallel will need
- int desccount = numDMAdescriptorsPerRow * ROWS_PER_FRAME;
-
- if (m_cfg.double_buff)
- {
+ if (m_cfg.double_buff) {
dma_bus.enable_double_dma_desc();
}
- dma_bus.allocate_dma_desc_memory(desccount);
-
- // point FB we can write to, to 0 / dmadesc_a
- fb = &frame_buffer[0];
-
- // Just os we know
- initialized = true;
-
- return true;
-
-} // end allocateDMAmemory()
-
-
-
-/*
-// Version 2.0 March 2023
-int MatrixPanel_I2S_DMA::create_descriptor_links(void *data, size_t size, bool dmadesc_b, bool countonly)
-{
- int len = size;
- uint8_t *data2 = (uint8_t *)data;
-
- int n = 0;
- while (len)
- {
- int dmalen = len;
- if (dmalen > DMA_MAX)
- dmalen = DMA_MAX;
-
- if (!countonly)
- dma_bus.create_dma_desc_link(data2, dmalen, dmadesc_b);
-
- len -= dmalen;
- data2 += dmalen;
- n++;
- }
-
- return n;
-}
-*/
-void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG &_cfg)
-{
-
- // lldesc_t *previous_dmadesc_a = 0;
- // lldesc_t *previous_dmadesc_b = 0;
- int current_dmadescriptor_offset = 0;
-
- // HACK: If we need to split the payload in 1/2 so that it doesn't breach DMA_MAX, lets do it by the colour_depth.
- /*
- int num_dma_payload_colour_depths = m_cfg.getPixelColorDepthBits();
- if (frame_buffer[0].rowBits[0]->getColorDepthSize() > DMA_MAX)
+ if ( !dma_bus.allocate_dma_desc_memory(dma_descriptions_required) )
{
- num_dma_payload_colour_depths = 1;
+ return false;
}
- */
- // Fill DMA linked lists for both frames (as in, halves of the HUB75 panel) in sequence (top to bottom)
- for (int row = 0; row < ROWS_PER_FRAME; row++)
- {
- // first set of data is LSB through MSB, single pass (IF TOTAL SIZE < DMA_MAX) - all colour bits are displayed once, which takes care of everything below and including LSBMSB_TRANSITION_BIT
- // NOTE: size must be less than DMA_MAX - worst case for library: 16-bpp with 256 pixels per row would exceed this, need to break into two
- // link_dma_desc(&dmadesc_a[current_dmadescriptor_offset], previous_dmadesc_a, dma_buff.rowBits[row]->getDataPtr(), dma_buff.rowBits[row]->size(num_dma_payload_colour_depths));
- // previous_dmadesc_a = &dmadesc_a[current_dmadescriptor_offset];
-
- dma_bus.create_dma_desc_link(frame_buffer[0].rowBits[row]->getDataPtr(0, 0), frame_buffer[0].rowBits[row]->getColorDepthSize(), false);
+ /***
+ * Step 4: Link up the DMA descriptors per the colour depth and rows.
+ */
+ int _dmadescriptor_count = 0; // for tracking
- if (m_cfg.double_buff)
+ fbs_required = 1; // (m_cfg.double_buff) ? 2 : 1;
+ for (int fb = 0; fb < (fbs_required); fb++)
+ {
+ for (int row = 0; row < ROWS_PER_FRAME; row++)
{
- dma_bus.create_dma_desc_link(frame_buffer[1].rowBits[row]->getDataPtr(0, 1), frame_buffer[1].rowBits[row]->getColorDepthSize(), true);
- }
+ //ESP_LOGV("I2S-DMA", ">>> Linking DMA descriptors for output row %d", row);
+
+ // Link and send all colour data, all passes of everything in one hit. 1 bit colour at least...
+ for (int dma_desc_all = 0; dma_desc_all < dma_descs_per_row_all_cdepths; dma_desc_all++)
+ {
+ size_t payload_bytes = (dma_desc_all == (dma_descs_per_row_all_cdepths-1)) ? last_dma_desc_bytes_all_cdepths:DMA_MAX;
+
+ // Log the current descriptor number and the payload size being used.
+ //ESP_LOGV("I2S-DMA", "Processing dma_desc_all: %d, payload_bytes: %zu", dma_desc_all, payload_bytes);
+
+ dma_bus.create_dma_desc_link(frame_buffer[fb].rowBits[row]->getDataPtr(0)+(dma_desc_all*(DMA_MAX/sizeof(ESP32_I2S_DMA_STORAGE_TYPE))), payload_bytes);
+ _dmadescriptor_count++;
+
+ // Log the updated descriptor count after each operation.
+ //ESP_LOGV("I2S-DMA", "Updated _dmadescriptor_count: %d", _dmadescriptor_count);
+ }
+
+ // Step 2: Handle additional descriptors for bits beyond the lsbMsbTransitionBit
+ for (int i = lsbMsbTransitionBit + 1; i < m_cfg.getPixelColorDepthBits(); i++)
+ {
+ // binary time division setup: we need 2 of bit (LSBMSB_TRANSITION_BIT + 1) four of (LSBMSB_TRANSITION_BIT + 2), etc
+ // because we sweep through to MSB each time, it divides the number of times we have to sweep in half (saving linked list RAM)
+ // we need 2^(i - LSBMSB_TRANSITION_BIT - 1) == 1 << (i - LSBMSB_TRANSITION_BIT - 1) passes from i to MSB
- current_dmadescriptor_offset++;
+ for (int k = 0; k < (1 << (i - lsbMsbTransitionBit - 1)); k++)
+ {
+ // Link and send all colour data, all passes of everything in one hit.
+ for (int dma_desc_1cdepth = 0; dma_desc_1cdepth < dma_descs_per_row_1cdepth; dma_desc_1cdepth++)
+ {
+ size_t payload_bytes = (dma_desc_1cdepth == (dma_descs_per_row_1cdepth-1)) ? last_dma_desc_bytes_1cdepth:DMA_MAX;
+
+ // Log the current bit and the corresponding payload size.
+ // ESP_LOGV("I2S-DMA", "Processing color depth bit: %d, payload_bytes: %zu", i, payload_bytes);
+
+ dma_bus.create_dma_desc_link(frame_buffer[fb].rowBits[row]->getDataPtr(i)+(dma_desc_1cdepth*(DMA_MAX/sizeof(ESP32_I2S_DMA_STORAGE_TYPE))), payload_bytes);
+ _dmadescriptor_count++;
+
+ // Log the updated descriptor count after each operation.
+ // ESP_LOGV("I2S-DMA", "Updated _dmadescriptor_count: %d", _dmadescriptor_count);
+ }
+ } // end K
+
+ } // end all other colour depth bits
+
- // If the number of pixels per row is too great for the size of a DMA payload, so we need to split what we were going to send above.
- if (frame_buffer[0].rowBits[0]->getColorDepthSize() > DMA_MAX)
- {
+ } // end all rows
+ } // end framebuffer loop
+
+ /*
+ #include <iostream>
+ #include <cmath> // For pow function (if needed, though bit shifts are better)
- for (int cd = 1; cd < m_cfg.getPixelColorDepthBits(); cd++)
- {
- dma_bus.create_dma_desc_link(frame_buffer[0].rowBits[row]->getDataPtr(cd, 0), frame_buffer[0].rowBits[row]->getColorDepthSize(1), false);
+ int main() {
- if (m_cfg.double_buff)
- {
- dma_bus.create_dma_desc_link(frame_buffer[1].rowBits[row]->getDataPtr(cd, 1), frame_buffer[1].rowBits[row]->getColorDepthSize(1), true);
- }
+ int colorDepthBits = 8;
+ int lsbMsbTransitionBit = 0; // Assuming lsbMsbTransitionBit is 0
- current_dmadescriptor_offset++;
+ // Step 2: Handle additional descriptors for bits beyond the lsbMsbTransitionBit
+ for (int i = lsbMsbTransitionBit + 1; i < colorDepthBits; i++) {
+ // Calculate the number of passes required
+ int passes = 1 << (i - lsbMsbTransitionBit - 1);
- } // additional linked list items
- } // row depth struct
+ std::cout << "Bit Level: " << i << ", Passes Required: " << passes << std::endl;
- for (int i = lsbMsbTransitionBit + 1; i < m_cfg.getPixelColorDepthBits(); i++)
- {
- // binary time division setup: we need 2 of bit (LSBMSB_TRANSITION_BIT + 1) four of (LSBMSB_TRANSITION_BIT + 2), etc
- // because we sweep through to MSB each time, it divides the number of times we have to sweep in half (saving linked list RAM)
- // we need 2^(i - LSBMSB_TRANSITION_BIT - 1) == 1 << (i - LSBMSB_TRANSITION_BIT - 1) passes from i to MSB
+ // Simulate the inner loop for the number of passes
+ for (int k = 0; k < passes; k++) {
+ std::cout << " Pass " << k + 1 << " for bit " << i << std::endl;
+ }
+ }
- for (int k = 0; k < (1 << (i - lsbMsbTransitionBit - 1)); k++)
- {
- dma_bus.create_dma_desc_link(frame_buffer[0].rowBits[row]->getDataPtr(i, 0), frame_buffer[0].rowBits[row]->getColorDepthSize(1), false);
-
- if (m_cfg.double_buff)
- {
- dma_bus.create_dma_desc_link(frame_buffer[1].rowBits[row]->getDataPtr(i, 1), frame_buffer[1].rowBits[row]->getColorDepthSize(1), true);
- }
-
- current_dmadescriptor_offset++;
+ return 0;
+ }
+ */
+
- } // end colour depth ^ 2 linked list
- } // end colour depth loop
+ ESP_LOGI("I2S-DMA", "%d DMA descriptors linked to buffer data vs %d required.", _dmadescriptor_count, dma_descriptions_required);
- } // end frame rows
+ /***
+ * Step 5: Set default framebuffer to fb[0]
+ */
- ESP_LOGI("I2S-DMA", "%d DMA descriptors linked to buffer data.", current_dmadescriptor_offset);
+ fb = &frame_buffer[0];
+
//
// Setup DMA and Output to GPIO
@@ -295,16 +298,14 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG &_cfg)
dma_bus.config(bus_cfg);
- dma_bus.init();
-
- 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("I2S-DMA", "DMA setup completed");
+
+ initialized = true;
+
+ return true;
-} // end initMatrixDMABuff
+} // end setupDMA
/* There are 'bits' set in the frameStruct that we simply don't need to set every single time we change a pixel / DMA buffer co-ordinate.
* For example, the bits that determine the address lines, we don't need to set these every time. Once they're in place, and assuming we
@@ -396,7 +397,7 @@ void IRAM_ATTR MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint16_t x_coord, uint
// Get the contents at this address,
// it would represent a vector pointing to the full row of pixels for the specified colour depth bit at Y coordinate
- ESP32_I2S_DMA_STORAGE_TYPE *p = getRowDataPtr(y_coord, colour_depth_idx, back_buffer_id);
+ ESP32_I2S_DMA_STORAGE_TYPE *p = getRowDataPtr(y_coord, colour_depth_idx);
// We need to update the correct uint16_t word in the rowBitStruct array pointing to a specific pixel at X - coordinate
p[x_coord] &= _colourbitclear; // reset RGB bits
@@ -454,7 +455,7 @@ void MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint8_t red, uint8_t green, uint
--matrix_frame_parallel_row;
// The destination for the pixel row bitstream
- ESP32_I2S_DMA_STORAGE_TYPE *p = getRowDataPtr(matrix_frame_parallel_row, colour_depth_idx, back_buffer_id);
+ ESP32_I2S_DMA_STORAGE_TYPE *p = getRowDataPtr(matrix_frame_parallel_row, colour_depth_idx);
// iterate pixels in a row
int x_coord = fb->rowBits[matrix_frame_parallel_row]->width;
@@ -495,7 +496,7 @@ void MatrixPanel_I2S_DMA::clearFrameBuffer(bool _buff_id)
{
--row_idx;
- ESP32_I2S_DMA_STORAGE_TYPE *row = fb->rowBits[row_idx]->getDataPtr(0, -1); // set pointer to the HEAD of a buffer holding data for the entire matrix row
+ ESP32_I2S_DMA_STORAGE_TYPE *row = fb->rowBits[row_idx]->getDataPtr(0); // set pointer to the HEAD of a buffer holding data for the entire matrix row
ESP32_I2S_DMA_STORAGE_TYPE abcde = (ESP32_I2S_DMA_STORAGE_TYPE)row_idx;
abcde <<= BITS_ADDR_OFFSET; // shift row y-coord to match ABCDE bits in vector from 8 to 12
@@ -584,7 +585,7 @@ void MatrixPanel_I2S_DMA::clearFrameBuffer(bool _buff_id)
--colouridx;
// switch pointer to a row for a specific colour index
- row = fb->rowBits[row_idx]->getDataPtr(colouridx, -1);
+ row = fb->rowBits[row_idx]->getDataPtr(colouridx);
// DP3246 needs the latch high for 3 clock cycles, so start 2 cycles earlier
if (m_cfg.driver == HUB75_I2S_CFG::DP3246_SM5368)
@@ -657,7 +658,7 @@ void MatrixPanel_I2S_DMA::brtCtrlOEv2(uint8_t brt, const int _buff_id)
brightness_in_x_pixels = (brightness_in_x_pixels >> 1) | (brightness_in_x_pixels & 1);
// switch pointer to a row for a specific color index
- ESP32_I2S_DMA_STORAGE_TYPE *row = fb->rowBits[row_idx]->getDataPtr(colouridx, _buff_id);
+ ESP32_I2S_DMA_STORAGE_TYPE *row = fb->rowBits[row_idx]->getDataPtr(colouridx);
// define range of Output Enable on the center of the row
int x_coord_max = (_width + brightness_in_x_pixels + 1) >> 1;
@@ -826,7 +827,7 @@ void MatrixPanel_I2S_DMA::hlineDMA(int16_t x_coord, int16_t y_coord, int16_t l,
// Get the contents at this address,
// it would represent a vector pointing to the full row of pixels for the specified colour depth bit at Y coordinate
- ESP32_I2S_DMA_STORAGE_TYPE *p = fb->rowBits[y_coord]->getDataPtr(colour_depth_idx, back_buffer_id);
+ ESP32_I2S_DMA_STORAGE_TYPE *p = fb->rowBits[y_coord]->getDataPtr(colour_depth_idx);
// inlined version works slower here, dunno why :(
// ESP32_I2S_DMA_STORAGE_TYPE *p = getRowDataPtr(y_coord, colour_depth_idx, back_buffer_id);
@@ -934,7 +935,7 @@ void MatrixPanel_I2S_DMA::vlineDMA(int16_t x_coord, int16_t y_coord, int16_t l,
// Get the contents at this address,
// it would represent a vector pointing to the full row of pixels for the specified colour depth bit at Y coordinate
// ESP32_I2S_DMA_STORAGE_TYPE *p = getRowDataPtr(_y, colour_depth_idx, back_buffer_id);
- ESP32_I2S_DMA_STORAGE_TYPE *p = fb->rowBits[_y]->getDataPtr(colour_depth_idx, back_buffer_id);
+ ESP32_I2S_DMA_STORAGE_TYPE *p = fb->rowBits[_y]->getDataPtr(colour_depth_idx);
p[x_coord] &= _colourbitclear; // reset RGB bits
p[x_coord] |= RGB_output_bits; // set new RGB bits
diff --git a/src/ESP32-HUB75-MatrixPanel-I2S-DMA.h b/src/ESP32-HUB75-MatrixPanel-I2S-DMA.h
index 43e6178..eccfae0 100644
--- a/src/ESP32-HUB75-MatrixPanel-I2S-DMA.h
+++ b/src/ESP32-HUB75-MatrixPanel-I2S-DMA.h
@@ -130,39 +130,56 @@
* Note: sizeof(data) must be multiple of 32 bits, as ESP32 DMA linked list buffer address pointer must be word-aligned
*/
struct rowBitStruct
+/**
+ * @struct rowBitStruct
+ * @brief Structure to hold row data for HUB75 matrix panel DMA operations
+ *
+ * @var width
+ * Width of the row in pixels
+ *
+ * @var colour_depth
+ * Number of color depths (i.e. copies of each row for each colur bitmask)
+ *
+ * @var data
+ * Pointer to DMA storage type array holding pixel data for the row
+ *
+ * @note Memory allocation differs based on target platform and configuration:
+ * - For ESP32-S3 with SPIRAM: Allocates aligned memory in SPIRAM
+ * - For other configurations: Allocates DMA-capable internal memory
+ */
{
const size_t width;
const uint8_t colour_depth;
- const bool double_buff;
+ //const bool double_buff;
ESP32_I2S_DMA_STORAGE_TYPE *data;
- /** @brief
- * Returns size (in bytes) of row of data vectorfor a SINGLE buff for the number of colour depths requested
+ /** @brief Returns size (in bytes) of a colour depth row data array
+ *
+ * @param single_color_depth
+ * - if true, returns size for a single color depth layer
+ * - if false, returns total size for all color depth layers for a row.
+ *
+ * @returns size_t - Size in bytes required for DMA buffer allocation
*
- * default - Returns full data vector size for a SINGLE buff.
- * You should only pass either PIXEL_COLOR_DEPTH_BITS or '1' to this
- *
*/
- size_t getColorDepthSize(uint8_t _dpth = 0)
+ size_t getColorDepthSize(bool single_color_depth)
{
- if (!_dpth)
- _dpth = colour_depth;
- return width * _dpth * sizeof(ESP32_I2S_DMA_STORAGE_TYPE);
+ int _cdepth = (single_color_depth) ? 1:colour_depth;
+ return width * _cdepth * sizeof(ESP32_I2S_DMA_STORAGE_TYPE);
};
+
/** @brief
* Returns pointer to the row's data vector beginning at pixel[0] for _dpth colour bit
*
* NOTE: this call might be very slow in loops. Due to poor instruction caching in esp32 it might be required a reread from flash
* every loop cycle, better use inlined #define instead in such cases
*/
- // inline ESP32_I2S_DMA_STORAGE_TYPE* getDataPtr(const uint8_t _dpth=0, const bool buff_id=0) { return &(data[_dpth*width + buff_id*(width*colour_depth)]); };
-
- // BUFFER ID VALUE IS NOW IGNORED!!!!
- inline ESP32_I2S_DMA_STORAGE_TYPE *getDataPtr(const uint8_t _dpth = 0, const bool buff_id = 0) { return &(data[_dpth * width]); };
+ inline ESP32_I2S_DMA_STORAGE_TYPE *getDataPtr(const uint8_t _dpth = 0) { return &(data[_dpth * width]); };
// constructor - allocates DMA-capable memory to hold the struct data
- rowBitStruct(const size_t _width, const uint8_t _depth, const bool _dbuff) : width(_width), colour_depth(_depth), double_buff(_dbuff)
+ //rowBitStruct(const size_t _width, const uint8_t _depth, const bool _dbuff) : width(_width), colour_depth(_depth), double_buff(_dbuff)
+ rowBitStruct(const size_t _width, const uint8_t _depth) : width(_width), colour_depth(_depth)
{
// #if defined(SPIRAM_FRAMEBUFFER) && defined (CONFIG_IDF_TARGET_ESP32S3)
@@ -170,12 +187,12 @@ struct rowBitStruct
// data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_aligned_alloc(64, size()+size()*double_buff, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
// No longer have double buffer in the same struct - have a different struct
- data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_aligned_alloc(64, getColorDepthSize(), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_aligned_alloc(64, getColorDepthSize(false), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
#else
// data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_malloc( size()+size()*double_buff, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
// No longer have double buffer in the same struct - have a different struct
- data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_malloc(getColorDepthSize(), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
+ data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_malloc(getColorDepthSize(false), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
#endif
}
@@ -411,6 +428,7 @@ public:
if (initialized)
return true; // we don't do this twice or more!
+
if (!config_set)
return false;
@@ -435,30 +453,32 @@ public:
#if defined(SPIRAM_DMA_BUFFER)
// Trick library into dropping colour depth slightly when using PSRAM.
- // Actual output clockrate override occurs in configureDMA
m_cfg.i2sspeed = HUB75_I2S_CFG::HZ_8M;
#endif
/* As DMA buffers are dynamically allocated, we must allocated in begin()
* Ref: https://github.com/espressif/arduino-esp32/issues/831
*/
- if (!allocateDMAmemory())
+ if (!setupDMA(m_cfg))
{
return false;
} // couldn't even get the basic ram required.
+
+ if (!initialized)
+ {
+ ESP_LOGE("being()", "MatrixPanel_I2S_DMA::begin() failed!");
+ }
// Flush the DMA buffers prior to configuring DMA - Avoid visual artefacts on boot.
resetbuffers(); // Must fill the DMA buffer with the initial output bit sequence or the panel will display garbage
- // Setup the ESP32 DMA Engine. Sprite_TM built this stuff.
- configureDMA(m_cfg); // DMA and I2S configuration and setup
+ flipDMABuffer(); // display back buffer 0, draw to 1, ignored if double buffering isn't enabled.
+
+ // Start output output
+ dma_bus.init();
+ dma_bus.dma_transfer_start();
- // showDMABuffer(); // show backbuf_id of 0
- if (!initialized)
- {
- ESP_LOGE("being()", "MatrixPanel_I2S_DMA::begin() failed!");
- }
return initialized;
}
@@ -754,11 +774,9 @@ protected:
// ------- PRIVATE -------
private:
- /* Calculate the memory available for DMA use, do some other stuff, and allocate accordingly */
- bool allocateDMAmemory();
- /* Setup the DMA Link List chain and initiate the ESP32 DMA engine */
- void configureDMA(const HUB75_I2S_CFG &opts);
+ /* Setup the DMA Link List chain and configure the ESP32 DMA + I2S or LCD peripheral */
+ bool setupDMA(const HUB75_I2S_CFG &opts);
/**
* pre-init procedures for specific drivers