This commit is contained in:
mrfaptastic 2023-03-18 20:12:45 +00:00
parent 9308b59445
commit 9d36abeeaf
6 changed files with 1299 additions and 1332 deletions

View file

@ -10,7 +10,10 @@
* when used in tight loops while method from struct could be flushed out of instruction cache between * when used in tight loops while method from struct could be flushed out of instruction cache between
* loop cycles do NOT forget about buff_id param if using this. * loop cycles do NOT forget about buff_id param if using this.
*/ */
#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)]) // #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])
/* We need to update the correct uint16_t in the rowBitStruct array, that gets sent out in parallel /* 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 * 16 bit parallel mode - Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering
@ -46,21 +49,23 @@
#endif #endif
*/ */
bool MatrixPanel_I2S_DMA::allocateDMAmemory() bool MatrixPanel_I2S_DMA::allocateDMAmemory()
{ {
ESP_LOGI("I2S-DMA", "Free heap: %d", heap_caps_get_free_size(MALLOC_CAP_INTERNAL)); 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)); 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 // 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) // lets allocate a chunk of memory for each row (a row could span multiple panels if chaining is in place)
dma_buff.rowBits.reserve(ROWS_PER_FRAME);
ESP_LOGI("I2S-DMA", "allocating rowBitStructs with pixel_color_depth_bits of %d", m_cfg.getPixelColorDepthBits()); 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 // iterate through number of rows, allocate memory for each
size_t allocated_fb_memory = 0; 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) 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(), m_cfg.double_buff);
@ -75,8 +80,9 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
} }
allocated_fb_memory += ptr->size(); allocated_fb_memory += ptr->size();
dma_buff.rowBits.emplace_back(ptr); // save new rowBitStruct into rows vector frame_buffer[fb].rowBits.emplace_back(ptr); // save new rowBitStruct into rows vector
++dma_buff.rows; ++frame_buffer[fb].rows;
}
} }
ESP_LOGI("I2S-DMA", "Allocating %d bytes memory for DMA BCM framebuffer(s).", allocated_fb_memory); ESP_LOGI("I2S-DMA", "Allocating %d bytes memory for DMA BCM framebuffer(s).", allocated_fb_memory);
@ -85,7 +91,8 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
ESP_LOGI("I2S-DMA", "Minimum visual refresh rate (scan rate from panel top to bottom) requested: %d Hz", m_cfg.min_refresh_rate); ESP_LOGI("I2S-DMA", "Minimum visual refresh rate (scan rate from panel top to bottom) requested: %d Hz", m_cfg.min_refresh_rate);
while(1) { while (1)
{
int psPerClock = 1000000000000UL / m_cfg.i2sspeed; 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;
@ -120,13 +127,13 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
#endif #endif
/*** /***
* Step 2a: lsbMsbTransition bit is now finalised - recalculate the DMA descriptor count required, which is used for * 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. * memory allocation of the DMA linked list memory structure.
*/ */
int numDMAdescriptorsPerRow = 1; int numDMAdescriptorsPerRow = 1;
for(int i=lsbMsbTransitionBit + 1; i < m_cfg.getPixelColorDepthBits(); i++) { for (int i = lsbMsbTransitionBit + 1; i < m_cfg.getPixelColorDepthBits(); i++)
{
numDMAdescriptorsPerRow += (1 << (i - lsbMsbTransitionBit - 1)); numDMAdescriptorsPerRow += (1 << (i - lsbMsbTransitionBit - 1));
} }
@ -134,7 +141,7 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
// Refer to 'DMA_LL_PAYLOAD_SPLIT' code in configureDMA() below to understand why this exists. // 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. // numDMAdescriptorsPerRow is also used to calculate descount which is super important in i2s_parallel_config_t SoC DMA setup.
if ( dma_buff.rowBits[0]->size() > DMA_MAX ) if (frame_buffer[0].rowBits[0]->size() > 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); 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);
@ -143,7 +150,6 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
// Note: If numDMAdescriptorsPerRow is even just one descriptor too large, DMA linked list will not correctly loop. // Note: If numDMAdescriptorsPerRow is even just one descriptor too large, DMA linked list will not correctly loop.
} }
/*** /***
* Step 3: Allocate memory for DMA linked list, linking up each framebuffer row in sequence for GPIO output. * Step 3: Allocate memory for DMA linked list, linking up each framebuffer row in sequence for GPIO output.
*/ */
@ -152,10 +158,15 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
desccount = numDMAdescriptorsPerRow * ROWS_PER_FRAME; desccount = numDMAdescriptorsPerRow * ROWS_PER_FRAME;
if (m_cfg.double_buff) if (m_cfg.double_buff)
{
dma_bus.enable_double_dma_desc(); dma_bus.enable_double_dma_desc();
}
dma_bus.allocate_dma_desc_memory(desccount); 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 // Just os we know
initialized = true; initialized = true;
@ -163,8 +174,6 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
} // end allocateDMAmemory() } // end allocateDMAmemory()
void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG &_cfg) void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG &_cfg)
{ {
@ -174,7 +183,8 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
// 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. // 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(); int num_dma_payload_colour_depths = m_cfg.getPixelColorDepthBits();
if ( dma_buff.rowBits[0]->size() > DMA_MAX ) { if (frame_buffer[0].rowBits[0]->size() > DMA_MAX)
{
num_dma_payload_colour_depths = 1; num_dma_payload_colour_depths = 1;
} }
@ -186,25 +196,26 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
// 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)); // 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]; // previous_dmadesc_a = &dmadesc_a[current_dmadescriptor_offset];
dma_bus.create_dma_desc_link( dma_buff.rowBits[row]->getDataPtr(0, 0), dma_buff.rowBits[row]->size(num_dma_payload_colour_depths), false); dma_bus.create_dma_desc_link(frame_buffer[0].rowBits[row]->getDataPtr(0, 0), frame_buffer[0].rowBits[row]->size(1), false);
if (m_cfg.double_buff) if (m_cfg.double_buff)
{ {
dma_bus.create_dma_desc_link( dma_buff.rowBits[row]->getDataPtr(0, 1), dma_buff.rowBits[row]->size(num_dma_payload_colour_depths), true); dma_bus.create_dma_desc_link(frame_buffer[1].rowBits[row]->getDataPtr(0, 1), frame_buffer[1].rowBits[row]->size(1), true);
} }
current_dmadescriptor_offset++; current_dmadescriptor_offset++;
// 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 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 ( dma_buff.rowBits[0]->size() > DMA_MAX ) if (frame_buffer[0].rowBits[0]->size() > DMA_MAX)
{ {
for (int cd = 1; cd < m_cfg.getPixelColorDepthBits(); cd++) for (int cd = 1; cd < m_cfg.getPixelColorDepthBits(); cd++)
{ {
dma_bus.create_dma_desc_link(dma_buff.rowBits[row]->getDataPtr(cd, 0), dma_buff.rowBits[row]->size(num_dma_payload_colour_depths), false); dma_bus.create_dma_desc_link(frame_buffer[0].rowBits[row]->getDataPtr(cd, 0), frame_buffer[0].rowBits[row]->size(1), false);
if (m_cfg.double_buff) { if (m_cfg.double_buff)
dma_bus.create_dma_desc_link(dma_buff.rowBits[row]->getDataPtr(cd, 1), dma_buff.rowBits[row]->size(num_dma_payload_colour_depths), true); {
dma_bus.create_dma_desc_link(frame_buffer[1].rowBits[row]->getDataPtr(cd, 1), frame_buffer[1].rowBits[row]->size(1), true);
} }
current_dmadescriptor_offset++; current_dmadescriptor_offset++;
@ -212,7 +223,6 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
} // additional linked list items } // additional linked list items
} // row depth struct } // row depth struct
for (int i = lsbMsbTransitionBit + 1; i < m_cfg.getPixelColorDepthBits(); i++) 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 // binary time division setup: we need 2 of bit (LSBMSB_TRANSITION_BIT + 1) four of (LSBMSB_TRANSITION_BIT + 2), etc
@ -221,10 +231,11 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
for (int k = 0; k < (1 << (i - lsbMsbTransitionBit - 1)); k++) for (int k = 0; k < (1 << (i - lsbMsbTransitionBit - 1)); k++)
{ {
dma_bus.create_dma_desc_link(dma_buff.rowBits[row]->getDataPtr(i, 0), dma_buff.rowBits[row]->size(m_cfg.getPixelColorDepthBits() - i), false); dma_bus.create_dma_desc_link(frame_buffer[0].rowBits[row]->getDataPtr(i, 0), frame_buffer[0].rowBits[row]->size(1), false);
if (m_cfg.double_buff) { if (m_cfg.double_buff)
dma_bus.create_dma_desc_link(dma_buff.rowBits[row]->getDataPtr(i, 1), dma_buff.rowBits[row]->size(m_cfg.getPixelColorDepthBits() - i), true ); {
dma_bus.create_dma_desc_link(frame_buffer[1].rowBits[row]->getDataPtr(i, 1), frame_buffer[1].rowBits[row]->size(1), true);
} }
current_dmadescriptor_offset++; current_dmadescriptor_offset++;
@ -278,7 +289,6 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
} // end initMatrixDMABuff } // end initMatrixDMABuff
/* 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. /* 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 * 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
* don't accidentally clear them, then we don't need to set them again. * don't accidentally clear them, then we don't need to set them again.
@ -300,12 +310,14 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
*/ */
void IRAM_ATTR MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint16_t x_coord, uint16_t y_coord, uint8_t red, uint8_t green, uint8_t blue) void IRAM_ATTR MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint16_t x_coord, uint16_t y_coord, uint8_t red, uint8_t green, uint8_t blue)
{ {
if ( !initialized ) return; if (!initialized)
return;
/* 1) Check that the co-ordinates are within range, or it'll break everything big time. /* 1) Check that the co-ordinates are within range, or it'll break everything big time.
* Valid co-ordinates are from 0 to (MATRIX_XXXX-1) * Valid co-ordinates are from 0 to (MATRIX_XXXX-1)
*/ */
if ( x_coord >= PIXELS_PER_ROW || y_coord >= m_cfg.mx_height) { if (x_coord >= PIXELS_PER_ROW || y_coord >= m_cfg.mx_height)
{
return; return;
} }
@ -338,10 +350,10 @@ uint16_t red16, green16, blue16;
*/ */
x_coord = ESP32_TX_FIFO_POSITION_ADJUST(x_coord); x_coord = ESP32_TX_FIFO_POSITION_ADJUST(x_coord);
uint16_t _colourbitclear = BITMASK_RGB1_CLEAR, _colourbitoffset = 0; uint16_t _colourbitclear = BITMASK_RGB1_CLEAR, _colourbitoffset = 0;
if (y_coord >= ROWS_PER_FRAME){ // if we are drawing to the bottom part of the panel if (y_coord >= ROWS_PER_FRAME)
{ // if we are drawing to the bottom part of the panel
_colourbitoffset = BITS_RGB2_OFFSET; _colourbitoffset = BITS_RGB2_OFFSET;
_colourbitclear = BITMASK_RGB2_CLEAR; _colourbitclear = BITMASK_RGB2_CLEAR;
y_coord -= ROWS_PER_FRAME; y_coord -= ROWS_PER_FRAME;
@ -349,7 +361,8 @@ uint16_t red16, green16, blue16;
// Iterating through colour depth bits, which we assume are 8 bits per RGB subpixel (24bpp) // Iterating through colour depth bits, which we assume are 8 bits per RGB subpixel (24bpp)
uint8_t colour_depth_idx = m_cfg.getPixelColorDepthBits(); uint8_t colour_depth_idx = m_cfg.getPixelColorDepthBits();
do { do
{
--colour_depth_idx; --colour_depth_idx;
uint16_t mask = PIXEL_COLOR_MASK_BIT(colour_depth_idx, MASK_OFFSET); uint16_t mask = PIXEL_COLOR_MASK_BIT(colour_depth_idx, MASK_OFFSET);
@ -364,12 +377,10 @@ uint16_t red16, green16, blue16;
RGB_output_bits |= (bool)(red16 & mask); // BGR RGB_output_bits |= (bool)(red16 & mask); // BGR
RGB_output_bits <<= _colourbitoffset; // shift colour bits to the required position RGB_output_bits <<= _colourbitoffset; // shift colour bits to the required position
// Get the contents at this address, // 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 // 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, back_buffer_id);
// We need to update the correct uint16_t word in the rowBitStruct array pointing to a specific pixel at X - coordinate // 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 p[x_coord] &= _colourbitclear; // reset RGB bits
p[x_coord] |= RGB_output_bits; // set new RGB bits p[x_coord] |= RGB_output_bits; // set new RGB bits
@ -381,11 +392,11 @@ uint16_t red16, green16, blue16;
} while (colour_depth_idx); // end of colour depth loop (8) } while (colour_depth_idx); // end of colour depth loop (8)
} // updateMatrixDMABuffer (specific co-ords change) } // updateMatrixDMABuffer (specific co-ords change)
/* Update the entire buffer with a single specific colour - quicker */ /* Update the entire buffer with a single specific colour - quicker */
void MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint8_t red, uint8_t green, uint8_t blue) void MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint8_t red, uint8_t green, uint8_t blue)
{ {
if ( !initialized ) return; if (!initialized)
return;
/* https://ledshield.wordpress.com/2012/11/13/led-brightness-to-your-eye-gamma-correction-no/ */ /* https://ledshield.wordpress.com/2012/11/13/led-brightness-to-your-eye-gamma-correction-no/ */
uint16_t red16, green16, blue16; uint16_t red16, green16, blue16;
@ -420,21 +431,22 @@ uint16_t red16, green16, blue16;
// Serial.printf("Fill with: 0x%#06x\n", RGB_output_bits); // Serial.printf("Fill with: 0x%#06x\n", RGB_output_bits);
// iterate rows // iterate rows
int matrix_frame_parallel_row = dma_buff.rowBits.size(); int matrix_frame_parallel_row = fb->rowBits.size();
do { do
{
--matrix_frame_parallel_row; --matrix_frame_parallel_row;
// The destination for the pixel row bitstream // 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, back_buffer_id);
// iterate pixels in a row // iterate pixels in a row
int x_coord=dma_buff.rowBits[matrix_frame_parallel_row]->width; int x_coord = fb->rowBits[matrix_frame_parallel_row]->width;
do { do
{
--x_coord; --x_coord;
p[x_coord] &= BITMASK_RGB12_CLEAR; // reset colour bits p[x_coord] &= BITMASK_RGB12_CLEAR; // reset colour bits
p[x_coord] |= RGB_output_bits; // set new colour bits p[x_coord] |= RGB_output_bits; // set new colour bits
#if defined(SPIRAM_DMA_BUFFER) #if defined(SPIRAM_DMA_BUFFER)
Cache_WriteBack_Addr((uint32_t)&p[x_coord], sizeof(ESP32_I2S_DMA_STORAGE_TYPE)); Cache_WriteBack_Addr((uint32_t)&p[x_coord], sizeof(ESP32_I2S_DMA_STORAGE_TYPE));
#endif #endif
@ -453,63 +465,74 @@ uint16_t red16, green16, blue16;
* This effectively clears buffers to blank BLACK and makes it ready to display output. * This effectively clears buffers to blank BLACK and makes it ready to display output.
* (Brightness control via OE bit manipulation is another case) - this must be done as well seperately! * (Brightness control via OE bit manipulation is another case) - this must be done as well seperately!
*/ */
void MatrixPanel_I2S_DMA::clearFrameBuffer(bool _buff_id){ void MatrixPanel_I2S_DMA::clearFrameBuffer(bool _buff_id)
{
if (!initialized) if (!initialized)
return; return;
// we start with iterating all rows in dma_buff structure // we start with iterating all rows in dma_buff structure
int row_idx = dma_buff.rowBits.size(); int row_idx = fb->rowBits.size();
do { do
{
--row_idx; --row_idx;
ESP32_I2S_DMA_STORAGE_TYPE* row = dma_buff.rowBits[row_idx]->getDataPtr(0, _buff_id); // 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, _buff_id); // 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; 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 abcde <<= BITS_ADDR_OFFSET; // shift row y-coord to match ABCDE bits in vector from 8 to 12
// get last pixel index in a row of all colourdepths // get last pixel index in a row of all colourdepths
int x_pixel = dma_buff.rowBits[row_idx]->width * dma_buff.rowBits[row_idx]->colour_depth; int x_pixel = fb->rowBits[row_idx]->width * fb->rowBits[row_idx]->colour_depth;
// Serial.printf(" from pixel %d, ", x_pixel); // Serial.printf(" from pixel %d, ", x_pixel);
// fill all x_pixels except colour_index[0] (LSB) ones, this also clears all colour data to 0's black // fill all x_pixels except colour_index[0] (LSB) ones, this also clears all colour data to 0's black
do { do
{
--x_pixel; --x_pixel;
if ( m_cfg.driver == HUB75_I2S_CFG::SM5266P) { if (m_cfg.driver == HUB75_I2S_CFG::SM5266P)
{
// modifications here for row shift register type SM5266P // modifications here for row shift register type SM5266P
// https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/164 // https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/164
row[x_pixel] = abcde & (0x18 << BITS_ADDR_OFFSET); // mask out the bottom 3 bits which are the clk di bk inputs row[x_pixel] = abcde & (0x18 << BITS_ADDR_OFFSET); // mask out the bottom 3 bits which are the clk di bk inputs
} else { }
else
{
row[ESP32_TX_FIFO_POSITION_ADJUST(x_pixel)] = abcde; row[ESP32_TX_FIFO_POSITION_ADJUST(x_pixel)] = abcde;
} }
} while(x_pixel!=dma_buff.rowBits[row_idx]->width && x_pixel); } while (x_pixel != fb->rowBits[row_idx]->width && x_pixel);
// colour_index[0] (LSB) x_pixels must be "marked" with a previous's row address, 'cause it is used to display // colour_index[0] (LSB) x_pixels must be "marked" with a previous's row address, 'cause it is used to display
// previous row while we pump in LSB's for a new row // previous row while we pump in LSB's for a new row
abcde = ((ESP32_I2S_DMA_STORAGE_TYPE)row_idx - 1) << BITS_ADDR_OFFSET; abcde = ((ESP32_I2S_DMA_STORAGE_TYPE)row_idx - 1) << BITS_ADDR_OFFSET;
do { do
{
--x_pixel; --x_pixel;
if ( m_cfg.driver == HUB75_I2S_CFG::SM5266P) { if (m_cfg.driver == HUB75_I2S_CFG::SM5266P)
{
// modifications here for row shift register type SM5266P // modifications here for row shift register type SM5266P
// https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/164 // https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/164
row[x_pixel] = abcde & (0x18 << BITS_ADDR_OFFSET); // mask out the bottom 3 bits which are the clk di bk inputs row[x_pixel] = abcde & (0x18 << BITS_ADDR_OFFSET); // mask out the bottom 3 bits which are the clk di bk inputs
} else { }
else
{
row[ESP32_TX_FIFO_POSITION_ADJUST(x_pixel)] = abcde; row[ESP32_TX_FIFO_POSITION_ADJUST(x_pixel)] = abcde;
} }
} while (x_pixel); } while (x_pixel);
// modifications here for row shift register type SM5266P // modifications here for row shift register type SM5266P
// https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/164 // https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/164
if ( m_cfg.driver == HUB75_I2S_CFG::SM5266P) { if (m_cfg.driver == HUB75_I2S_CFG::SM5266P)
{
uint16_t serialCount; uint16_t serialCount;
uint16_t latch; uint16_t latch;
x_pixel = dma_buff.rowBits[row_idx]->width - 16; // come back 8*2 pixels to allow for 8 writes x_pixel = fb->rowBits[row_idx]->width - 16; // come back 8*2 pixels to allow for 8 writes
serialCount = 8; serialCount = 8;
do{ do
{
serialCount--; serialCount--;
latch = row[x_pixel] | (((((ESP32_I2S_DMA_STORAGE_TYPE)row_idx) % 8) == serialCount) << 1) << BITS_ADDR_OFFSET; // data on 'B' latch = row[x_pixel] | (((((ESP32_I2S_DMA_STORAGE_TYPE)row_idx) % 8) == serialCount) << 1) << BITS_ADDR_OFFSET; // data on 'B'
row[x_pixel++] = latch | (0x05 << BITS_ADDR_OFFSET); // clock high on 'A'and BK high for update row[x_pixel++] = latch | (0x05 << BITS_ADDR_OFFSET); // clock high on 'A'and BK high for update
@ -517,153 +540,67 @@ void MatrixPanel_I2S_DMA::clearFrameBuffer(bool _buff_id){
} while (serialCount); } while (serialCount);
} // end SM5266P } // end SM5266P
// let's set LAT/OE control bits for specific pixels in each colour_index subrows // let's set LAT/OE control bits for specific pixels in each colour_index subrows
// Need to consider the original ESP32's (WROOM) DMA TX FIFO reordering of bytes... // Need to consider the original ESP32's (WROOM) DMA TX FIFO reordering of bytes...
uint8_t colouridx = dma_buff.rowBits[row_idx]->colour_depth; uint8_t colouridx = fb->rowBits[row_idx]->colour_depth;
do { do
{
--colouridx; --colouridx;
// switch pointer to a row for a specific colour index // switch pointer to a row for a specific colour index
row = dma_buff.rowBits[row_idx]->getDataPtr(colouridx, _buff_id); row = fb->rowBits[row_idx]->getDataPtr(colouridx, _buff_id);
row[ESP32_TX_FIFO_POSITION_ADJUST(dma_buff.rowBits[row_idx]->width - 1)] |= BIT_LAT; // -1 pixel to compensate array index starting at 0 row[ESP32_TX_FIFO_POSITION_ADJUST(fb->rowBits[row_idx]->width - 1)] |= BIT_LAT; // -1 pixel to compensate array index starting at 0
// ESP32_TX_FIFO_POSITION_ADJUST(dma_buff.rowBits[row_idx]->width - 1) // ESP32_TX_FIFO_POSITION_ADJUST(dma_buff.rowBits[row_idx]->width - 1)
// need to disable OE before/after latch to hide row transition // need to disable OE before/after latch to hide row transition
// Should be one clock or more before latch, otherwise can get ghosting // Should be one clock or more before latch, otherwise can get ghosting
uint8_t _blank = m_cfg.latch_blanking; uint8_t _blank = m_cfg.latch_blanking;
do { do
{
--_blank; --_blank;
row[ESP32_TX_FIFO_POSITION_ADJUST(0 + _blank)] |= BIT_OE; // disable output row[ESP32_TX_FIFO_POSITION_ADJUST(0 + _blank)] |= BIT_OE; // disable output
row[ESP32_TX_FIFO_POSITION_ADJUST(dma_buff.rowBits[row_idx]->width - 1)] |= BIT_OE; // disable output row[ESP32_TX_FIFO_POSITION_ADJUST(fb->rowBits[row_idx]->width - 1)] |= BIT_OE; // disable output
row[ESP32_TX_FIFO_POSITION_ADJUST(dma_buff.rowBits[row_idx]->width - _blank - 1)] |= BIT_OE; // (LAT pulse is (width-2) -1 pixel to compensate array index starting at 0 row[ESP32_TX_FIFO_POSITION_ADJUST(fb->rowBits[row_idx]->width - _blank - 1)] |= BIT_OE; // (LAT pulse is (width-2) -1 pixel to compensate array index starting at 0
} while (_blank); } while (_blank);
} while (colouridx); } while (colouridx);
#if defined(SPIRAM_DMA_BUFFER) #if defined(SPIRAM_DMA_BUFFER)
Cache_WriteBack_Addr((uint32_t)row, sizeof(ESP32_I2S_DMA_STORAGE_TYPE) * ((dma_buff.rowBits[row_idx]->width * dma_buff.rowBits[row_idx]->colour_depth)-1)) ; Cache_WriteBack_Addr((uint32_t)row, sizeof(ESP32_I2S_DMA_STORAGE_TYPE) * ((fb->rowBits[row_idx]->width * fb->rowBits[row_idx]->colour_depth) - 1));
#endif #endif
} while (row_idx); } while (row_idx);
} }
/**
* @brief - reset OE bits in DMA buffer in a way to control brightness
* @param brt - brightness level from 0 to row_width
* @param _buff_id - buffer id to control
*/
/*
// Depreciated
void MatrixPanel_I2S_DMA::brtCtrlOE(int brt, const bool _buff_id){
if (!initialized)
return;
if (brt > PIXELS_PER_ROW - (MAX_LAT_BLANKING + 2)) // can't control values larger than (row_width - latch_blanking) to avoid ongoing issues being raised about brightness and ghosting.
brt = PIXELS_PER_ROW - (MAX_LAT_BLANKING + 2); // +2 for a bit of buffer...
if (brt < 0)
brt = 0;
// start with iterating all rows in dma_buff structure
int row_idx = dma_buff.rowBits.size();
do {
--row_idx;
// let's set OE control bits for specific pixels in each colour_index subrows
uint8_t colouridx = dma_buff.rowBits[row_idx]->colour_depth;
do {
--colouridx;
// switch pointer to a row for a specific colour index
ESP32_I2S_DMA_STORAGE_TYPE* row = dma_buff.rowBits[row_idx]->getDataPtr(colouridx, _buff_id);
int x_coord = dma_buff.rowBits[row_idx]->width;
do {
--x_coord;
// clear OE bit for all other pixels
row[ESP32_TX_FIFO_POSITION_ADJUST(x_coord)] &= BITMASK_OE_CLEAR;
// Brightness control via OE toggle - disable matrix output at specified x_coord
if((colouridx > lsbMsbTransitionBit || !colouridx) && ((x_coord) >= brt)){
row[ESP32_TX_FIFO_POSITION_ADJUST(x_coord)] |= BIT_OE; // Disable output after this point.
continue;
}
// special case for the bits *after* LSB through (lsbMsbTransitionBit) - OE is output after data is shifted, so need to set OE to fractional brightness
if(colouridx && colouridx <= lsbMsbTransitionBit) {
// divide brightness in half for each bit below lsbMsbTransitionBit
int lsbBrightness = brt >> (lsbMsbTransitionBit - colouridx + 1);
if((x_coord) >= lsbBrightness) {
row[ESP32_TX_FIFO_POSITION_ADJUST(x_coord)] |= BIT_OE; // Disable output after this point.
continue;
}
}
} while(x_coord);
// need to disable OE before/after latch to hide row transition
// Should be one clock or more before latch, otherwise can get ghosting
uint8_t _blank = m_cfg.latch_blanking;
do {
--_blank;
row[ESP32_TX_FIFO_POSITION_ADJUST(0 + _blank)] |= BIT_OE;
//row[0 + _blank] |= BIT_OE;
// no need, has been done already
//row[dma_buff.rowBits[row_idx]->width - _blank - 3 ] |= BIT_OE; // (LAT pulse is (width-2) -1 pixel to compensate array index starting at 0
} while (_blank);
} while(colouridx);
// switch pointer to a row for a specific colour index
#if defined(SPIRAM_DMA_BUFFER)
ESP32_I2S_DMA_STORAGE_TYPE* row_hack = dma_buff.rowBits[row_idx]->getDataPtr(colouridx, _buff_id);
Cache_WriteBack_Addr((uint32_t)row_hack, sizeof(ESP32_I2S_DMA_STORAGE_TYPE) * ((dma_buff.rowBits[row_idx]->width * dma_buff.rowBits[row_idx]->colour_depth)-1)) ;
#endif
} while(row_idx);
}
*/
/** /**
* @brief - reset OE bits in DMA buffer in a way to control brightness * @brief - reset OE bits in DMA buffer in a way to control brightness
* @param brt - brightness level from 0 to 255 - NOT MATRIX_WIDTH * @param brt - brightness level from 0 to 255 - NOT MATRIX_WIDTH
* @param _buff_id - buffer id to control * @param _buff_id - buffer id to control
*/ */
void MatrixPanel_I2S_DMA::brtCtrlOEv2(uint8_t brt, const int _buff_id) { void MatrixPanel_I2S_DMA::brtCtrlOEv2(uint8_t brt, const int _buff_id)
{
if (!initialized) if (!initialized)
return; return;
uint8_t _blank = m_cfg.latch_blanking; // don't want to inadvertantly blast over this uint8_t _blank = m_cfg.latch_blanking; // don't want to inadvertantly blast over this
uint8_t _depth = dma_buff.rowBits[0]->colour_depth; uint8_t _depth = fb->rowBits[0]->colour_depth;
uint16_t _width = dma_buff.rowBits[0]->width; uint16_t _width = fb->rowBits[0]->width;
// start with iterating all rows in dma_buff structure // start with iterating all rows in dma_buff structure
int row_idx = dma_buff.rowBits.size(); int row_idx = fb->rowBits.size();
do { do
{
--row_idx; --row_idx;
// let's set OE control bits for specific pixels in each color_index subrows // let's set OE control bits for specific pixels in each color_index subrows
uint8_t colouridx = _depth; uint8_t colouridx = _depth;
do { do
{
--colouridx; --colouridx;
char bitplane = (2 * _depth - colouridx) % _depth; char bitplane = (2 * _depth - colouridx) % _depth;
@ -675,13 +612,14 @@ 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); brightness_in_x_pixels = (brightness_in_x_pixels >> 1) | (brightness_in_x_pixels & 1);
// switch pointer to a row for a specific color index // switch pointer to a row for a specific color index
ESP32_I2S_DMA_STORAGE_TYPE* row = dma_buff.rowBits[row_idx]->getDataPtr(colouridx, _buff_id); ESP32_I2S_DMA_STORAGE_TYPE *row = fb->rowBits[row_idx]->getDataPtr(colouridx, _buff_id);
// define range of Output Enable on the center of the row // define range of Output Enable on the center of the row
int x_coord_max = (_width + brightness_in_x_pixels + 1) >> 1; int x_coord_max = (_width + brightness_in_x_pixels + 1) >> 1;
int x_coord_min = (_width - brightness_in_x_pixels + 0) >> 1; int x_coord_min = (_width - brightness_in_x_pixels + 0) >> 1;
int x_coord = _width; int x_coord = _width;
do { do
{
--x_coord; --x_coord;
// (the check is already including "blanking" ) // (the check is already including "blanking" )
@ -700,44 +638,54 @@ void MatrixPanel_I2S_DMA::brtCtrlOEv2(uint8_t brt, const int _buff_id) {
// switch pointer to a row for a specific colour index // switch pointer to a row for a specific colour index
#if defined(SPIRAM_DMA_BUFFER) #if defined(SPIRAM_DMA_BUFFER)
ESP32_I2S_DMA_STORAGE_TYPE* row_hack = dma_buff.rowBits[row_idx]->getDataPtr(colouridx, _buff_id); ESP32_I2S_DMA_STORAGE_TYPE *row_hack = fb->rowBits[row_idx]->getDataPtr(colouridx, _buff_id);
Cache_WriteBack_Addr((uint32_t)row_hack, sizeof(ESP32_I2S_DMA_STORAGE_TYPE) * ((dma_buff.rowBits[row_idx]->width * dma_buff.rowBits[row_idx]->colour_depth)-1)) ; Cache_WriteBack_Addr((uint32_t)row_hack, sizeof(ESP32_I2S_DMA_STORAGE_TYPE) * ((fb->rowBits[row_idx]->width * fb->rowBits[row_idx]->colour_depth) - 1));
#endif #endif
} while (row_idx); } while (row_idx);
} }
/* /*
* overload for compatibility * overload for compatibility
*/ */
bool MatrixPanel_I2S_DMA::begin(int r1, int g1, int b1, int r2, int g2, int b2, int a, int b, int c, int d, int e, int lat, int oe, int clk) { bool MatrixPanel_I2S_DMA::begin(int r1, int g1, int b1, int r2, int g2, int b2, int a, int b, int c, int d, int e, int lat, int oe, int clk)
if(initialized) return true; {
if (initialized)
return true;
// RGB // RGB
m_cfg.gpio.r1 = r1; m_cfg.gpio.g1 = g1; m_cfg.gpio.b1 = b1; m_cfg.gpio.r1 = r1;
m_cfg.gpio.r2 = r2; m_cfg.gpio.g2 = g2; m_cfg.gpio.b2 = b2; m_cfg.gpio.g1 = g1;
m_cfg.gpio.b1 = b1;
m_cfg.gpio.r2 = r2;
m_cfg.gpio.g2 = g2;
m_cfg.gpio.b2 = b2;
// Line Select // Line Select
m_cfg.gpio.a = a; m_cfg.gpio.b = b; m_cfg.gpio.c = c; m_cfg.gpio.a = a;
m_cfg.gpio.d = d; m_cfg.gpio.e = e; m_cfg.gpio.b = b;
m_cfg.gpio.c = c;
m_cfg.gpio.d = d;
m_cfg.gpio.e = e;
// Clock & Control // Clock & Control
m_cfg.gpio.lat = lat; m_cfg.gpio.oe = oe; m_cfg.gpio.clk = clk; m_cfg.gpio.lat = lat;
m_cfg.gpio.oe = oe;
m_cfg.gpio.clk = clk;
return begin(); return begin();
} }
bool MatrixPanel_I2S_DMA::begin(const HUB75_I2S_CFG& cfg){ bool MatrixPanel_I2S_DMA::begin(const HUB75_I2S_CFG &cfg)
if(initialized) return true; {
if (initialized)
return true;
if(!setCfg(cfg)) return false; if (!setCfg(cfg))
return false;
return begin(); return begin();
} }
/** /**
* @brief - Sets how many clock cycles to blank OE before/after LAT signal change * @brief - Sets how many clock cycles to blank OE before/after LAT signal change
* @param uint8_t pulses - clocks before/after OE * @param uint8_t pulses - clocks before/after OE
@ -745,7 +693,8 @@ bool MatrixPanel_I2S_DMA::begin(const HUB75_I2S_CFG& cfg){
* Max is MAX_LAT_BLANKING * Max is MAX_LAT_BLANKING
* @returns - new value for m_cfg.latch_blanking * @returns - new value for m_cfg.latch_blanking
*/ */
uint8_t MatrixPanel_I2S_DMA::setLatBlanking(uint8_t pulses){ uint8_t MatrixPanel_I2S_DMA::setLatBlanking(uint8_t pulses)
{
if (pulses > MAX_LAT_BLANKING) if (pulses > MAX_LAT_BLANKING)
pulses = MAX_LAT_BLANKING; pulses = MAX_LAT_BLANKING;
@ -759,7 +708,6 @@ uint8_t MatrixPanel_I2S_DMA::setLatBlanking(uint8_t pulses){
return m_cfg.latch_blanking; return m_cfg.latch_blanking;
} }
#ifndef NO_FAST_FUNCTIONS #ifndef NO_FAST_FUNCTIONS
/** /**
* @brief - update DMA buff drawing horizontal line at specified coordinates * @brief - update DMA buff drawing horizontal line at specified coordinates
@ -768,7 +716,8 @@ uint8_t MatrixPanel_I2S_DMA::setLatBlanking(uint8_t pulses){
* @param l - line length * @param l - line length
* @param r,g,b, - RGB888 colour * @param r,g,b, - RGB888 colour
*/ */
void MatrixPanel_I2S_DMA::hlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, uint8_t red, uint8_t green, uint8_t blue){ void MatrixPanel_I2S_DMA::hlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, uint8_t red, uint8_t green, uint8_t blue)
{
if (!initialized) if (!initialized)
return; return;
@ -778,7 +727,6 @@ void MatrixPanel_I2S_DMA::hlineDMA(int16_t x_coord, int16_t y_coord, int16_t l,
l = x_coord < 0 ? l + x_coord : l; l = x_coord < 0 ? l + x_coord : l;
x_coord = x_coord < 0 ? 0 : x_coord; x_coord = x_coord < 0 ? 0 : x_coord;
l = ((x_coord + l) >= PIXELS_PER_ROW) ? (PIXELS_PER_ROW - x_coord) : l; l = ((x_coord + l) >= PIXELS_PER_ROW) ? (PIXELS_PER_ROW - x_coord) : l;
// if (x_coord+l > PIXELS_PER_ROW) // if (x_coord+l > PIXELS_PER_ROW)
@ -798,7 +746,8 @@ uint16_t red16, green16, blue16;
uint16_t _colourbitclear = BITMASK_RGB1_CLEAR, _colourbitoffset = 0; uint16_t _colourbitclear = BITMASK_RGB1_CLEAR, _colourbitoffset = 0;
if (y_coord >= ROWS_PER_FRAME){ // if we are drawing to the bottom part of the panel if (y_coord >= ROWS_PER_FRAME)
{ // if we are drawing to the bottom part of the panel
_colourbitoffset = BITS_RGB2_OFFSET; _colourbitoffset = BITS_RGB2_OFFSET;
_colourbitclear = BITMASK_RGB2_CLEAR; _colourbitclear = BITMASK_RGB2_CLEAR;
y_coord -= ROWS_PER_FRAME; y_coord -= ROWS_PER_FRAME;
@ -806,7 +755,8 @@ uint16_t red16, green16, blue16;
// Iterating through colour depth bits (8 iterations) // Iterating through colour depth bits (8 iterations)
uint8_t colour_depth_idx = m_cfg.getPixelColorDepthBits(); uint8_t colour_depth_idx = m_cfg.getPixelColorDepthBits();
do { do
{
--colour_depth_idx; --colour_depth_idx;
// let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer // let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer
@ -830,12 +780,13 @@ uint16_t red16, green16, blue16;
// Get the contents at this address, // 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 // 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 = dma_buff.rowBits[y_coord]->getDataPtr(colour_depth_idx, back_buffer_id); ESP32_I2S_DMA_STORAGE_TYPE *p = fb->rowBits[y_coord]->getDataPtr(colour_depth_idx, back_buffer_id);
// inlined version works slower here, dunno why :( // inlined version works slower here, dunno why :(
// 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, back_buffer_id);
int16_t _l = l; int16_t _l = l;
do { // iterate pixels in a row do
{ // iterate pixels in a row
int16_t _x = x_coord + --_l; int16_t _x = x_coord + --_l;
/* /*
@ -855,7 +806,6 @@ uint16_t red16, green16, blue16;
} while (colour_depth_idx); // end of colour depth loop (8) } while (colour_depth_idx); // end of colour depth loop (8)
} // hlineDMA() } // hlineDMA()
/** /**
* @brief - update DMA buff drawing vertical line at specified coordinates * @brief - update DMA buff drawing vertical line at specified coordinates
* @param x_coord - line start coordinate x * @param x_coord - line start coordinate x
@ -863,7 +813,8 @@ uint16_t red16, green16, blue16;
* @param l - line length * @param l - line length
* @param r,g,b, - RGB888 colour * @param r,g,b, - RGB888 colour
*/ */
void MatrixPanel_I2S_DMA::vlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, uint8_t red, uint8_t green, uint8_t blue){ void MatrixPanel_I2S_DMA::vlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, uint8_t red, uint8_t green, uint8_t blue)
{
if (!initialized) if (!initialized)
return; return;
@ -899,7 +850,8 @@ uint16_t red16, green16, blue16;
x_coord = ESP32_TX_FIFO_POSITION_ADJUST(x_coord); x_coord = ESP32_TX_FIFO_POSITION_ADJUST(x_coord);
uint8_t colour_depth_idx = m_cfg.getPixelColorDepthBits(); uint8_t colour_depth_idx = m_cfg.getPixelColorDepthBits();
do { // Iterating through colour depth bits (8 iterations) do
{ // Iterating through colour depth bits (8 iterations)
--colour_depth_idx; --colour_depth_idx;
// let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer // let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer
@ -923,9 +875,11 @@ uint16_t red16, green16, blue16;
int16_t _l = 0, _y = y_coord; int16_t _l = 0, _y = y_coord;
uint16_t _colourbitclear = BITMASK_RGB1_CLEAR; uint16_t _colourbitclear = BITMASK_RGB1_CLEAR;
do { // iterate pixels in a column do
{ // iterate pixels in a column
if (_y >= ROWS_PER_FRAME){ // if y-coord overlapped bottom-half panel if (_y >= ROWS_PER_FRAME)
{ // if y-coord overlapped bottom-half panel
_y -= ROWS_PER_FRAME; _y -= ROWS_PER_FRAME;
_colourbitclear = BITMASK_RGB2_CLEAR; _colourbitclear = BITMASK_RGB2_CLEAR;
RGB_output_bits <<= BITS_RGB2_OFFSET; RGB_output_bits <<= BITS_RGB2_OFFSET;
@ -934,7 +888,7 @@ uint16_t red16, green16, blue16;
// Get the contents at this address, // 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 // 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 = getRowDataPtr(_y, colour_depth_idx, back_buffer_id);
ESP32_I2S_DMA_STORAGE_TYPE *p = dma_buff.rowBits[_y]->getDataPtr(colour_depth_idx, back_buffer_id); ESP32_I2S_DMA_STORAGE_TYPE *p = fb->rowBits[_y]->getDataPtr(colour_depth_idx, back_buffer_id);
p[x_coord] &= _colourbitclear; // reset RGB bits p[x_coord] &= _colourbitclear; // reset RGB bits
p[x_coord] |= RGB_output_bits; // set new RGB bits p[x_coord] |= RGB_output_bits; // set new RGB bits
@ -943,7 +897,6 @@ uint16_t red16, green16, blue16;
} while (colour_depth_idx); // end of colour depth loop (8) } while (colour_depth_idx); // end of colour depth loop (8)
} // vlineDMA() } // vlineDMA()
/** /**
* @brief - update DMA buff drawing a rectangular at specified coordinates * @brief - update DMA buff drawing a rectangular at specified coordinates
* this works much faster than multiple consecutive per-pixel calls to updateMatrixDMABuffer() * this works much faster than multiple consecutive per-pixel calls to updateMatrixDMABuffer()
@ -953,19 +906,25 @@ uint16_t red16, green16, blue16;
* @param uint8_t g - RGB888 colour * @param uint8_t g - RGB888 colour
* @param uint8_t b - RGB888 colour * @param uint8_t b - RGB888 colour
*/ */
void MatrixPanel_I2S_DMA::fillRectDMA(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t r, uint8_t g, uint8_t b){ void MatrixPanel_I2S_DMA::fillRectDMA(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t r, uint8_t g, uint8_t b)
{
// h-lines are >2 times faster than v-lines // h-lines are >2 times faster than v-lines
// so will use it only for tall rects with h >2w // so will use it only for tall rects with h >2w
if (h>2*w){ if (h > 2 * w)
{
// draw using v-lines // draw using v-lines
do { do
{
--w; --w;
vlineDMA(x + w, y, h, r, g, b); vlineDMA(x + w, y, h, r, g, b);
} while (w); } while (w);
} else { }
else
{
// draw using h-lines // draw using h-lines
do { do
{
--h; --h;
hlineDMA(x, y + h, w, r, g, b); hlineDMA(x, y + h, w, r, g, b);
} while (h); } while (h);

View file

@ -52,7 +52,6 @@
#define CHAIN_LENGTH 1 // Number of modules chained together, i.e. 4 panels chained result in virtualmatrix 64x4=256 px long #define CHAIN_LENGTH 1 // Number of modules chained together, i.e. 4 panels chained result in virtualmatrix 64x4=256 px long
#endif #endif
// Interesting Fact: We end up using a uint16_t to send data in parallel to the HUB75... but // Interesting Fact: We end up using a uint16_t to send data in parallel to the HUB75... but
// given we only map to 14 physical output wires/bits, we waste 2 bits. // given we only map to 14 physical output wires/bits, we waste 2 bits.
@ -129,7 +128,8 @@
/** @brief - Structure holds raw DMA data to drive TWO full rows of pixels spanning through all chained modules /** @brief - Structure holds raw DMA data to drive TWO full rows of pixels spanning through all chained modules
* Note: sizeof(data) must be multiple of 32 bits, as ESP32 DMA linked list buffer address pointer must be word-aligned * 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
{
const size_t width; const size_t width;
const uint8_t colour_depth; const uint8_t colour_depth;
const bool double_buff; const bool double_buff;
@ -143,24 +143,34 @@ struct rowBitStruct {
* default - returns full data vector size for a SINGLE buff * default - returns full data vector size for a SINGLE buff
* *
*/ */
size_t size(uint8_t _dpth=0 ) { if (!_dpth) _dpth = colour_depth; return width * _dpth * sizeof(ESP32_I2S_DMA_STORAGE_TYPE); }; size_t size(uint8_t _dpth = 0)
{
if (!_dpth)
_dpth = colour_depth;
return width * _dpth * sizeof(ESP32_I2S_DMA_STORAGE_TYPE);
};
/** @brief - returns pointer to the row's data vector beginning at pixel[0] for _dpth colour bit /** @brief - returns pointer to the row's data vector beginning at pixel[0] for _dpth colour bit
* default - returns pointer to the data vector's head * default - returns pointer to the data vector's head
* NOTE: this call might be very slow in loops. Due to poor instruction caching in esp32 it might be required a reread from flash * 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 * 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)]); }; // 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]); };
// constructor - allocates DMA-capable memory to hold the struct data // 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)
{
// #if defined(SPIRAM_FRAMEBUFFER) && defined (CONFIG_IDF_TARGET_ESP32S3) // #if defined(SPIRAM_FRAMEBUFFER) && defined (CONFIG_IDF_TARGET_ESP32S3)
#if defined(SPIRAM_DMA_BUFFER) #if defined(SPIRAM_DMA_BUFFER)
// #pragma message "Enabling PSRAM / SPIRAM for frame buffer."
// ESP_LOGI("rowBitStruct", "Allocated DMA BitBuffer from PSRAM (SPIRAM)"); // data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_aligned_alloc(64, size()+size()*double_buff, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
//data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_malloc( size()+size()*double_buff, MALLOC_CAP_SPIRAM);
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, size(), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
/* /*
if (!psramFound()) if (!psramFound())
{ {
@ -168,15 +178,16 @@ struct rowBitStruct {
} }
*/ */
#else #else
data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_malloc( size()+size()*double_buff, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); // 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(size(), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
// ESP_LOGI("rowBitStruct", "Allocated DMA BitBuffer from regular (and limited) SRAM"); // ESP_LOGI("rowBitStruct", "Allocated DMA BitBuffer from regular (and limited) SRAM");
#endif #endif
} }
~rowBitStruct() { delete data; } ~rowBitStruct() { delete data; }
}; };
/* frameStruct /* frameStruct
* Note: A 'frameStruct' contains ALL the data for a full-frame (i.e. BOTH 2x16-row frames are * Note: A 'frameStruct' contains ALL the data for a full-frame (i.e. BOTH 2x16-row frames are
* are contained in parallel within the one uint16_t that is sent in parallel to the HUB75). * are contained in parallel within the one uint16_t that is sent in parallel to the HUB75).
@ -184,7 +195,8 @@ struct rowBitStruct {
* This structure isn't actually allocated in one memory block anymore, as the library now allocates * This structure isn't actually allocated in one memory block anymore, as the library now allocates
* memory per row (per rowBits) instead. * memory per row (per rowBits) instead.
*/ */
struct frameStruct { struct frameStruct
{
uint8_t rows = 0; // number of rows held in current frame, not used actually, just to keep the idea of struct uint8_t rows = 0; // number of rows held in current frame, not used actually, just to keep the idea of struct
std::vector<std::shared_ptr<rowBitStruct>> rowBits; std::vector<std::shared_ptr<rowBitStruct>> rowBits;
}; };
@ -216,8 +228,7 @@ static const uint16_t DRAM_ATTR lumConvTab[]={
31946, 32360, 32777, 33197, 33622, 34049, 34481, 34916, 35354, 35797, 36243, 36692, 37146, 37603, 38064, 38528, 31946, 32360, 32777, 33197, 33622, 34049, 34481, 34916, 35354, 35797, 36243, 36692, 37146, 37603, 38064, 38528,
38996, 39469, 39945, 40424, 40908, 41395, 41886, 42382, 42881, 43383, 43890, 44401, 44916, 45434, 45957, 46484, 38996, 39469, 39945, 40424, 40908, 41395, 41886, 42382, 42881, 43383, 43890, 44401, 44916, 45434, 45957, 46484,
47014, 47549, 48088, 48630, 49177, 49728, 50283, 50842, 51406, 51973, 52545, 53120, 53700, 54284, 54873, 55465, 47014, 47549, 48088, 48630, 49177, 49728, 50283, 50842, 51406, 51973, 52545, 53120, 53700, 54284, 54873, 55465,
56062, 56663, 57269, 57878, 58492, 59111, 59733, 60360, 60992, 61627, 62268, 62912, 63561, 64215, 64873, 65535 56062, 56663, 57269, 57878, 58492, 59111, 59733, 60360, 60992, 61627, 62268, 62912, 63561, 64215, 64873, 65535};
};
#endif #endif
/** @brief - configuration values for HUB75_I2S driver /** @brief - configuration values for HUB75_I2S driver
@ -225,19 +236,33 @@ static const uint16_t DRAM_ATTR lumConvTab[]={
* an initialization values when creating an instance of MatrixPanel_I2S_DMA object. * an initialization values when creating an instance of MatrixPanel_I2S_DMA object.
* All params have it's default values. * All params have it's default values.
*/ */
struct HUB75_I2S_CFG { struct HUB75_I2S_CFG
{
/** /**
* Enumeration of hardware-specific chips * Enumeration of hardware-specific chips
* used to drive matrix modules * used to drive matrix modules
*/ */
enum shift_driver {SHIFTREG=0, FM6124, FM6126A, ICN2038S, MBI5124, SM5266P}; enum shift_driver
{
SHIFTREG = 0,
FM6124,
FM6126A,
ICN2038S,
MBI5124,
SM5266P
};
/** /**
* I2S clock speed selector * I2S clock speed selector
*/ */
enum clk_speed {HZ_8M=8000000, HZ_10M=10000000, HZ_15M=15000000, HZ_20M=20000000}; enum clk_speed
{
HZ_8M = 8000000,
HZ_10M = 10000000,
HZ_15M = 15000000,
HZ_20M = 20000000
};
// //
// Members must be in order of declaration or it breaks Arduino compiling due to strict checking. // Members must be in order of declaration or it breaks Arduino compiling due to strict checking.
@ -253,7 +278,10 @@ struct HUB75_I2S_CFG {
uint16_t chain_length; uint16_t chain_length;
// GPIO Mapping // GPIO Mapping
struct i2s_pins{ int8_t r1, g1, b1, r2, g2, b2, a, b, c, d, e, lat, oe, clk; } gpio; struct i2s_pins
{
int8_t r1, g1, b1, r2, g2, b2, a, b, c, d, e, lat, oe, clk;
} gpio;
// Matrix driver chip type - default is a plain shift register // Matrix driver chip type - default is a plain shift register
shift_driver driver; shift_driver driver;
@ -286,7 +314,6 @@ struct HUB75_I2S_CFG {
// 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; uint8_t min_refresh_rate;
// struct constructor // struct constructor
HUB75_I2S_CFG( HUB75_I2S_CFG(
uint16_t _w = MATRIX_WIDTH, uint16_t _w = MATRIX_WIDTH,
@ -296,44 +323,38 @@ struct HUB75_I2S_CFG {
R1_PIN_DEFAULT, G1_PIN_DEFAULT, B1_PIN_DEFAULT, R2_PIN_DEFAULT, G2_PIN_DEFAULT, B2_PIN_DEFAULT, 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, A_PIN_DEFAULT, B_PIN_DEFAULT, C_PIN_DEFAULT, D_PIN_DEFAULT, E_PIN_DEFAULT,
LAT_PIN_DEFAULT, OE_PIN_DEFAULT, CLK_PIN_DEFAULT}, LAT_PIN_DEFAULT, OE_PIN_DEFAULT, CLK_PIN_DEFAULT},
shift_driver _drv = SHIFTREG, shift_driver _drv = SHIFTREG, bool _dbuff = false, clk_speed _i2sspeed = HZ_15M,
bool _dbuff = false,
clk_speed _i2sspeed = HZ_15M,
uint8_t _latblk = DEFAULT_LAT_BLANKING, // Anything > 1 seems to cause artefacts on ICS panels uint8_t _latblk = DEFAULT_LAT_BLANKING, // Anything > 1 seems to cause artefacts on ICS panels
bool _clockphase = true, 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)
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); setPixelColorDepthBits(_pixel_color_depth_bits);
} }
// pixel_color_depth_bits must be between 12 and 2, and mask_offset needs to be calculated accordently // pixel_color_depth_bits must be between 12 and 2, and mask_offset needs to be calculated accordently
// so they have to be private with getter (and setter) // so they have to be private with getter (and setter)
void setPixelColorDepthBits(uint8_t _pixel_color_depth_bits){ void setPixelColorDepthBits(uint8_t _pixel_color_depth_bits)
if(_pixel_color_depth_bits > PIXEL_COLOR_DEPTH_BITS_MAX || _pixel_color_depth_bits < 2){ {
if (_pixel_color_depth_bits > PIXEL_COLOR_DEPTH_BITS_MAX || _pixel_color_depth_bits < 2)
{
if(_pixel_color_depth_bits > PIXEL_COLOR_DEPTH_BITS_MAX){ if (_pixel_color_depth_bits > PIXEL_COLOR_DEPTH_BITS_MAX)
{
pixel_color_depth_bits = PIXEL_COLOR_DEPTH_BITS_MAX; pixel_color_depth_bits = PIXEL_COLOR_DEPTH_BITS_MAX;
}else{ }
else
{
pixel_color_depth_bits = 2; pixel_color_depth_bits = 2;
} }
ESP_LOGW("HUB75_I2S_CFG", "Invalid pixel_color_depth_bits (%d): 2 <= pixel_color_depth_bits <= %d, choosing nearest valid %d", _pixel_color_depth_bits, PIXEL_COLOR_DEPTH_BITS_MAX, pixel_color_depth_bits); ESP_LOGW("HUB75_I2S_CFG", "Invalid pixel_color_depth_bits (%d): 2 <= pixel_color_depth_bits <= %d, choosing nearest valid %d", _pixel_color_depth_bits, PIXEL_COLOR_DEPTH_BITS_MAX, pixel_color_depth_bits);
}else{ }
else
{
pixel_color_depth_bits = _pixel_color_depth_bits; pixel_color_depth_bits = _pixel_color_depth_bits;
} }
} }
uint8_t getPixelColorDepthBits(){ uint8_t getPixelColorDepthBits()
{
return pixel_color_depth_bits; return pixel_color_depth_bits;
} }
@ -343,20 +364,20 @@ struct HUB75_I2S_CFG {
uint8_t pixel_color_depth_bits; uint8_t pixel_color_depth_bits;
}; // end of structure HUB75_I2S_CFG }; // end of structure HUB75_I2S_CFG
/***************************************************************************************/ /***************************************************************************************/
#ifdef USE_GFX_ROOT #ifdef USE_GFX_ROOT
class MatrixPanel_I2S_DMA : public GFX { class MatrixPanel_I2S_DMA : public GFX
{
#elif !defined NO_GFX #elif !defined NO_GFX
class MatrixPanel_I2S_DMA : public Adafruit_GFX { class MatrixPanel_I2S_DMA : public Adafruit_GFX
{
#else #else
class MatrixPanel_I2S_DMA { class MatrixPanel_I2S_DMA
{
#endif #endif
// ------- PUBLIC ------- // ------- PUBLIC -------
public: public:
/** /**
* MatrixPanel_I2S_DMA * MatrixPanel_I2S_DMA
* *
@ -369,7 +390,8 @@ class MatrixPanel_I2S_DMA {
#elif !defined NO_GFX #elif !defined NO_GFX
: Adafruit_GFX(MATRIX_WIDTH, MATRIX_HEIGHT) : Adafruit_GFX(MATRIX_WIDTH, MATRIX_HEIGHT)
#endif #endif
{} {
}
/** /**
* MatrixPanel_I2S_DMA * MatrixPanel_I2S_DMA
@ -388,10 +410,13 @@ class MatrixPanel_I2S_DMA {
} }
/* Propagate the DMA pin configuration, allocate DMA buffs and start data output, initially blank */ /* Propagate the DMA pin configuration, allocate DMA buffs and start data output, initially blank */
bool begin(){ bool begin()
{
if (initialized) return true; // we don't do this twice or more! if (initialized)
if(!config_set) return false; return true; // we don't do this twice or more!
if (!config_set)
return false;
ESP_LOGI("begin()", "Using GPIO %d for R1_PIN", m_cfg.gpio.r1); ESP_LOGI("begin()", "Using GPIO %d for R1_PIN", m_cfg.gpio.r1);
ESP_LOGI("begin()", "Using GPIO %d for G1_PIN", m_cfg.gpio.g1); ESP_LOGI("begin()", "Using GPIO %d for G1_PIN", m_cfg.gpio.g1);
@ -408,7 +433,6 @@ class MatrixPanel_I2S_DMA {
ESP_LOGI("begin()", "Using GPIO %d for OE_PIN", m_cfg.gpio.oe); ESP_LOGI("begin()", "Using GPIO %d for OE_PIN", m_cfg.gpio.oe);
ESP_LOGI("begin()", "Using GPIO %d for CLK_PIN", m_cfg.gpio.clk); ESP_LOGI("begin()", "Using GPIO %d for CLK_PIN", m_cfg.gpio.clk);
// initialize some specific panel drivers // initialize some specific panel drivers
if (m_cfg.driver) if (m_cfg.driver)
shiftDriver(m_cfg); shiftDriver(m_cfg);
@ -422,8 +446,10 @@ class MatrixPanel_I2S_DMA {
/* As DMA buffers are dynamically allocated, we must allocated in begin() /* As DMA buffers are dynamically allocated, we must allocated in begin()
* Ref: https://github.com/espressif/arduino-esp32/issues/831 * Ref: https://github.com/espressif/arduino-esp32/issues/831
*/ */
if ( !allocateDMAmemory() ) { return false; } // couldn't even get the basic ram required. if (!allocateDMAmemory())
{
return false;
} // couldn't even get the basic ram required.
// Flush the DMA buffers prior to configuring DMA - Avoid visual artefacts on boot. // 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 resetbuffers(); // Must fill the DMA buffer with the initial output bit sequence or the panel will display garbage
@ -433,22 +459,21 @@ class MatrixPanel_I2S_DMA {
// showDMABuffer(); // show backbuf_id of 0 // showDMABuffer(); // show backbuf_id of 0
if (!initialized) { if (!initialized)
{
ESP_LOGE("being()", "MatrixPanel_I2S_DMA::begin() failed!"); ESP_LOGE("being()", "MatrixPanel_I2S_DMA::begin() failed!");
} }
return initialized; return initialized;
} }
// Obj destructor // Obj destructor
~MatrixPanel_I2S_DMA(){ ~MatrixPanel_I2S_DMA()
{
dma_bus.release(); dma_bus.release();
} }
/* /*
* overload for compatibility * overload for compatibility
*/ */
@ -469,7 +494,8 @@ class MatrixPanel_I2S_DMA {
* @brief - override Adafruit's FastVLine * @brief - override Adafruit's FastVLine
* this works faster than multiple consecutive pixel by pixel drawPixel() call * this works faster than multiple consecutive pixel by pixel drawPixel() call
*/ */
virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color){ virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color)
{
uint8_t r, g, b; uint8_t r, g, b;
color565to888(color, r, g, b); color565to888(color, r, g, b);
startWrite(); startWrite();
@ -484,7 +510,8 @@ class MatrixPanel_I2S_DMA {
endWrite(); endWrite();
} }
// 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)
{
int16_t w = 1; int16_t w = 1;
transform(x, y, w, h); transform(x, y, w, h);
if (h > w) if (h > w)
@ -497,7 +524,8 @@ class MatrixPanel_I2S_DMA {
* @brief - override Adafruit's FastHLine * @brief - override Adafruit's FastHLine
* this works faster than multiple consecutive pixel by pixel drawPixel() call * this works faster than multiple consecutive pixel by pixel drawPixel() call
*/ */
virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color){ virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color)
{
uint8_t r, g, b; uint8_t r, g, b;
color565to888(color, r, g, b); color565to888(color, r, g, b);
startWrite(); startWrite();
@ -512,7 +540,8 @@ class MatrixPanel_I2S_DMA {
endWrite(); endWrite();
} }
// 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)
{
int16_t h = 1; int16_t h = 1;
transform(x, y, w, h); transform(x, y, w, h);
if (h > w) if (h > w)
@ -525,7 +554,8 @@ class MatrixPanel_I2S_DMA {
* @brief - override Adafruit's fillRect * @brief - override Adafruit's fillRect
* this works much faster than multiple consecutive per-pixel drawPixel() calls * this works much faster than multiple consecutive per-pixel drawPixel() calls
*/ */
virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color){ virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
uint8_t r, g, b; uint8_t r, g, b;
color565to888(color, r, g, b); color565to888(color, r, g, b);
startWrite(); startWrite();
@ -534,7 +564,8 @@ class MatrixPanel_I2S_DMA {
endWrite(); endWrite();
} }
// 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)
{
startWrite(); startWrite();
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);
@ -569,14 +600,26 @@ class MatrixPanel_I2S_DMA {
*/ */
static void color565to888(const uint16_t color, uint8_t &r, uint8_t &g, uint8_t &b); static void color565to888(const uint16_t color, uint8_t &r, uint8_t &g, uint8_t &b);
inline void flipDMABuffer() inline void flipDMABuffer()
{ {
if ( !m_cfg.double_buff) { return; } if (!m_cfg.double_buff)
{
return;
}
if (back_buffer_id == 0) // back buffer is 0 (dmadesc_a)
{
fb = &frame_buffer[1];
}
else
{
fb = &frame_buffer[0];
}
// while (active_gfx_writes) { } // wait a bit ?
// initialized = false;
dma_bus.flip_dma_output_buffer(back_buffer_id); dma_bus.flip_dma_output_buffer(back_buffer_id);
back_buffer_id ^= 1;
// initialized = true; // initialized = true;
/* /*
@ -593,7 +636,6 @@ class MatrixPanel_I2S_DMA {
// Wait before we allow any writing to the buffer. Stop flicker. // Wait before we allow any writing to the buffer. Stop flicker.
while(i2s_parallel_is_previous_buffer_free() == false) { } while(i2s_parallel_is_previous_buffer_free() == false) { }
*/ */
} }
/** /**
@ -610,11 +652,10 @@ class MatrixPanel_I2S_DMA {
brightness = b; brightness = b;
brtCtrlOEv2(b, 0); brtCtrlOEv2(b, 0);
if (m_cfg.double_buff) { if (m_cfg.double_buff)
{
brtCtrlOEv2(b, 1); brtCtrlOEv2(b, 1);
} }
} }
// Takes a value that is between 0 and MATRIX_WIDTH-1 // Takes a value that is between 0 and MATRIX_WIDTH-1
@ -655,7 +696,6 @@ class MatrixPanel_I2S_DMA {
// setPanelBrightness(b * PIXELS_PER_ROW / 256); // setPanelBrightness(b * PIXELS_PER_ROW / 256);
} }
/** /**
* @brief - Sets how many clock cycles to blank OE before/after LAT signal change * @brief - Sets how many clock cycles to blank OE before/after LAT signal change
* @param uint8_t pulses - clocks before/after OE * @param uint8_t pulses - clocks before/after OE
@ -671,8 +711,10 @@ class MatrixPanel_I2S_DMA {
*/ */
const HUB75_I2S_CFG &getCfg() const { return m_cfg; }; const HUB75_I2S_CFG &getCfg() const { return m_cfg; };
inline bool setCfg(const HUB75_I2S_CFG& cfg){ inline bool setCfg(const HUB75_I2S_CFG &cfg)
if(initialized) return false; {
if (initialized)
return false;
m_cfg = cfg; m_cfg = cfg;
PIXELS_PER_ROW = m_cfg.mx_width * m_cfg.chain_length; PIXELS_PER_ROW = m_cfg.mx_width * m_cfg.chain_length;
@ -686,26 +728,27 @@ class MatrixPanel_I2S_DMA {
/** /**
* Stop the ESP32 DMA Engine. Screen will forever be black until next ESP reboot. * Stop the ESP32 DMA Engine. Screen will forever be black until next ESP reboot.
*/ */
void stopDMAoutput() { void stopDMAoutput()
{
resetbuffers(); resetbuffers();
// i2s_parallel_stop_dma(ESP32_I2S_DEVICE); // i2s_parallel_stop_dma(ESP32_I2S_DEVICE);
dma_bus.dma_transfer_stop(); dma_bus.dma_transfer_stop();
} }
void startWrite() { void startWrite()
{
// ESP_LOGI("TAG", "startWrite() called"); // ESP_LOGI("TAG", "startWrite() called");
active_gfx_writes++; active_gfx_writes++;
} }
void endWrite()
void endWrite() { {
active_gfx_writes--; active_gfx_writes--;
} }
// ------- PROTECTED ------- // ------- PROTECTED -------
// those might be useful for child classes, like VirtualMatrixPanel // those might be useful for child classes, like VirtualMatrixPanel
protected: protected:
/** /**
* @brief - clears and reinitializes colour/control data in DMA buffs * @brief - clears and reinitializes colour/control data in DMA buffs
* When allocated, DMA buffs might be dirty, so we need to blank it and initialize ABCDE,LAT,OE control bits. * When allocated, DMA buffs might be dirty, so we need to blank it and initialize ABCDE,LAT,OE control bits.
@ -725,19 +768,20 @@ class MatrixPanel_I2S_DMA {
/** /**
* wipes DMA buffer(s) and reset all colour/service bits * wipes DMA buffer(s) and reset all colour/service bits
*/ */
inline void resetbuffers(){ inline void resetbuffers()
{
clearFrameBuffer(); // flipDMABuffer();
brtCtrlOEv2(brightness, 0); fb = &frame_buffer[0];
if (m_cfg.double_buff){ clearFrameBuffer(0); // buffer ID is not used
clearFrameBuffer(1); brtCtrlOEv2(brightness, 0); // buffer ID is not used
brtCtrlOEv2(brightness, 1);
fb = &frame_buffer[1];
clearFrameBuffer(1); // buffer ID is not used
brtCtrlOEv2(brightness, 1); // buffer ID is not used
} }
}
#ifndef NO_FAST_FUNCTIONS #ifndef NO_FAST_FUNCTIONS
/** /**
* @brief - update DMA buff drawing horizontal line at specified coordinates * @brief - update DMA buff drawing horizontal line at specified coordinates
@ -771,7 +815,6 @@ class MatrixPanel_I2S_DMA {
// ------- PRIVATE ------- // ------- PRIVATE -------
private: private:
/* Calculate the memory available for DMA use, do some other stuff, and allocate accordingly */ /* Calculate the memory available for DMA use, do some other stuff, and allocate accordingly */
bool allocateDMAmemory(); bool allocateDMAmemory();
@ -810,28 +853,47 @@ class MatrixPanel_I2S_DMA {
* @param w - rectangular width * @param w - rectangular width
* @param h - rectangular height * @param h - rectangular height
*/ */
void transform(int16_t &x, int16_t &y, int16_t &w, int16_t &h){ void transform(int16_t &x, int16_t &y, int16_t &w, int16_t &h)
{
#ifndef NO_GFX #ifndef NO_GFX
int16_t t; int16_t t;
switch (rotation) { switch (rotation)
case 1: t = _height - 1 - y - ( h - 1 ); y = x; x = t; t = h; h = w; w = t; return; {
case 2: x = _width - 1 - x - ( w - 1 ); y = _height - 1 - y - ( h - 1 ); return; case 1:
case 3: t = y; y = _width - 1 - x - ( w - 1 ); x = t; t = h; h = w; w = t; return; t = _height - 1 - y - (h - 1);
y = x;
x = t;
t = h;
h = w;
w = t;
return;
case 2:
x = _width - 1 - x - (w - 1);
y = _height - 1 - y - (h - 1);
return;
case 3:
t = y;
y = _width - 1 - x - (w - 1);
x = t;
t = h;
h = w;
w = t;
return;
} }
#endif #endif
}; };
public: public:
/** /**
* Contains the resulting refresh rate (scan rate) that will be achieved * Contains the resulting refresh rate (scan rate) that will be achieved
* based on the i2sspeed, colour depth and min_refresh_rate requested. * based on the i2sspeed, colour depth and min_refresh_rate requested.
*/ */
int calculated_refresh_rate = 0; int calculated_refresh_rate = 0;
protected: 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;
@ -842,7 +904,10 @@ class MatrixPanel_I2S_DMA {
* Since it's dimensions is unknown prior to class initialization, we just declare it here as empty struct and will do all allocations later. * Since it's dimensions is unknown prior to class initialization, we just declare it here as empty struct and will do all allocations later.
* Refer to rowBitStruct to get the idea of it's internal structure * Refer to rowBitStruct to get the idea of it's internal structure
*/ */
frameStruct dma_buff; // frameStruct dma_buff;
frameStruct frame_buffer[2];
frameStruct *fb; // What framebuffer we are writing pixel changes to? (pointer to either frame_buffer[0] or frame_buffer[1] basically )
// ESP 32 DMA Linked List descriptor // ESP 32 DMA Linked List descriptor
int desccount = 0; int desccount = 0;
@ -878,7 +943,8 @@ class MatrixPanel_I2S_DMA {
* @param uint16_t colour - RGB565 input colour * @param uint16_t colour - RGB565 input colour
* @param uint8_t &r, &g, &b - refs to variables where converted colours would be emplaced * @param uint8_t &r, &g, &b - refs to variables where converted colours would be emplaced
*/ */
inline void MatrixPanel_I2S_DMA::color565to888(const uint16_t color, uint8_t &r, uint8_t &g, uint8_t &b){ inline void MatrixPanel_I2S_DMA::color565to888(const uint16_t color, uint8_t &r, uint8_t &g, uint8_t &b)
{
r = ((((color >> 11) & 0x1F) * 527) + 23) >> 6; r = ((((color >> 11) & 0x1F) * 527) + 23) >> 6;
g = ((((color >> 5) & 0x3F) * 259) + 33) >> 6; g = ((((color >> 5) & 0x3F) * 259) + 33) >> 6;
b = (((color & 0x1F) * 527) + 23) >> 6; b = (((color & 0x1F) * 527) + 23) >> 6;
@ -929,19 +995,21 @@ inline void MatrixPanel_I2S_DMA::fillScreen(CRGB color)
} }
#endif #endif
// Pass 8-bit (each) R,G,B, get back 16-bit packed colour // Pass 8-bit (each) R,G,B, get back 16-bit packed colour
// https://github.com/squix78/ILI9341Buffer/blob/master/ILI9341_SPI.cpp // https://github.com/squix78/ILI9341Buffer/blob/master/ILI9341_SPI.cpp
inline uint16_t MatrixPanel_I2S_DMA::color565(uint8_t r, uint8_t g, uint8_t b) { inline uint16_t MatrixPanel_I2S_DMA::color565(uint8_t r, uint8_t g, uint8_t b)
{
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
} }
// Promote 3/3/3 RGB to Adafruit_GFX 5/6/5 RRRrrGGGgggBBBbb // Promote 3/3/3 RGB to Adafruit_GFX 5/6/5 RRRrrGGGgggBBBbb
inline uint16_t MatrixPanel_I2S_DMA::color333(uint8_t r, uint8_t g, uint8_t b) { inline uint16_t MatrixPanel_I2S_DMA::color333(uint8_t r, uint8_t g, uint8_t b)
{
return ((r & 0x7) << 13) | ((r & 0x6) << 10) | ((g & 0x7) << 8) | ((g & 0x7) << 5) | ((b & 0x7) << 2) | ((b & 0x6) >> 1); return ((r & 0x7) << 13) | ((r & 0x6) << 10) | ((g & 0x7) << 8) | ((g & 0x7) << 5) | ((b & 0x7) << 2) | ((b & 0x6) >> 1);
} }
inline void MatrixPanel_I2S_DMA::drawIcon (int *ico, int16_t x, int16_t y, int16_t cols, int16_t rows) { inline void MatrixPanel_I2S_DMA::drawIcon(int *ico, int16_t x, int16_t y, int16_t cols, int16_t rows)
{
/* drawIcon draws a C style bitmap. /* drawIcon draws a C style bitmap.
// Example 10x5px bitmap of a yellow sun // Example 10x5px bitmap of a yellow sun
// //
@ -959,18 +1027,17 @@ inline void MatrixPanel_I2S_DMA::drawIcon (int *ico, int16_t x, int16_t y, int16
*/ */
int i, j; int i, j;
for (i = 0; i < rows; i++) { for (i = 0; i < rows; i++)
for (j = 0; j < cols; j++) { {
for (j = 0; j < cols; j++)
{
drawPixel(x + j, y + i, (uint16_t)ico[i * cols + j]); drawPixel(x + j, y + i, (uint16_t)ico[i * cols + j]);
} }
} }
} }
#endif #endif
// Credits: Louis Beaudoin <https://github.com/pixelmatix/SmartMatrix/tree/teensylc> // Credits: Louis Beaudoin <https://github.com/pixelmatix/SmartMatrix/tree/teensylc>
// and Sprite_TM: https://www.esp32.com/viewtopic.php?f=17&t=3188 and https://www.esp32.com/viewtopic.php?f=13&t=3256 // and Sprite_TM: https://www.esp32.com/viewtopic.php?f=17&t=3188 and https://www.esp32.com/viewtopic.php?f=13&t=3256

View file

@ -28,54 +28,28 @@ Modified heavily for the ESP32 HUB75 DMA library by:
#include <driver/periph_ctrl.h> #include <driver/periph_ctrl.h>
#include <soc/gpio_sig_map.h> #include <soc/gpio_sig_map.h>
#include <Arduino.h> // Need to make sure thi is uncommented to get ESP_LOG output on (Arduino) Serial output!!!! #include <Arduino.h> // Need to make sure this is uncommented to get ESP_LOG output on (Arduino) Serial output!!!!
#include <esp_err.h> #include <esp_err.h>
#include <esp_log.h> #include <esp_log.h>
// Get CPU freq function. // Get CPU freq function.
#include <soc/rtc.h> #include <soc/rtc.h>
/*
callback shiftCompleteCallback;
void setShiftCompleteCallback(callback f) {
shiftCompleteCallback = f;
}
volatile int previousBufferOutputLoopCount = 0;
volatile bool previousBufferFree = true; volatile bool previousBufferFree = true;
static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default) // Todo: handle IS20? (this is hard coded for I2S1 only)
static void IRAM_ATTR i2s_isr(void* arg) {
SET_PERI_REG_BITS(I2S_INT_CLR_REG(ESP32_I2S_DEVICE), I2S_OUT_EOF_INT_CLR_V, 1, I2S_OUT_EOF_INT_CLR_S); REG_WRITE(I2S_INT_CLR_REG(1), (REG_READ(I2S_INT_RAW_REG(1)) & 0xffffffc0) | 0x3f);
// at this point, the previously active buffer is free, go ahead and write to it
previousBufferFree = true; previousBufferFree = true;
} // end irq_hndlr
*/
volatile int DRAM_ATTR active_dma_buffer_output_count = 0;
void IRAM_ATTR irq_hndlr(void* arg) {
// Clear flag so we can get retriggered
SET_PERI_REG_BITS(I2S_INT_CLR_REG(ESP32_I2S_DEVICE), I2S_OUT_EOF_INT_CLR_V, 1, I2S_OUT_EOF_INT_CLR_S);
active_dma_buffer_output_count++;
/*
if ( active_dma_buffer_output_count++ )
{
// Disable DMA chain EOF interrupt until next requested flipbuffer.
// Otherwise we're needlessly generating interrupts we don't care about.
//SET_PERI_REG_BITS(I2S_INT_ENA_REG(ESP32_I2S_DEVICE), I2S_OUT_EOF_INT_ENA_V, 0, I2S_OUT_EOF_INT_ENA_S);
active_dma_buffer_output_count = 0;
} }
*/
} // end irq_hndlr bool DRAM_ATTR i2s_parallel_is_previous_buffer_free() {
return previousBufferFree;
}
// Static // Static
i2s_dev_t* getDev() i2s_dev_t* getDev()
@ -221,22 +195,6 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default)
ESP_LOGD("ESP32/S2", "Requested output clock frequency: %d Mhz", (freq/1000000)); ESP_LOGD("ESP32/S2", "Requested output clock frequency: %d Mhz", (freq/1000000));
// What is the current CPU frequency? // What is the current CPU frequency?
/*
rtc_cpu_freq_config_t conf;
rtc_clk_cpu_freq_get_config(&conf);
auto source_freq = conf.source_freq_mhz;
ESP_LOGD("ESP32/S2", "PLL (source) frequency: %d", source_freq);
ESP_LOGD("ESP32/S2", "CPU frequency: %d", conf.freq_mhz);
*/
/*
if(_div_num < 2 || _div_num > 16) {
return false;
}
*/
// Calculate clock divider for ESP32-S2 // Calculate clock divider for ESP32-S2
#if defined (CONFIG_IDF_TARGET_ESP32S2) #if defined (CONFIG_IDF_TARGET_ESP32S2)
@ -415,21 +373,12 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default)
/* 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.
*/ */
if (_double_dma_buffer) {
// Get ISR setup // setup I2S Interrupt
esp_err_t err = esp_intr_alloc(irq_source, SET_PERI_REG_BITS(I2S_INT_ENA_REG(1), I2S_OUT_EOF_INT_ENA_V, 1, I2S_OUT_EOF_INT_ENA_S);
(int)(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1), // allocate a level 1 intterupt: lowest priority, as ISR isn't urgent and may take a long time to complete
irq_hndlr, NULL, NULL); esp_intr_alloc(ETS_I2S1_INTR_SOURCE, (int)(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1), i2s_isr, NULL, NULL);
if(err) {
ESP_LOGE("ESP32/S2", "init() Failed to setup interrupt request handeler.");
return false;
}
// Don't do this here. Don't enable just yet.
// dev->int_ena.out_eof = 1;
}
#if defined (CONFIG_IDF_TARGET_ESP32S2) #if defined (CONFIG_IDF_TARGET_ESP32S2)
@ -611,21 +560,16 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default)
} // end } // end
void Bus_Parallel16::flip_dma_output_buffer(int &current_back_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)
//_dev->int_ena.out_eof = 1;
_dev->int_ena.out_eof = 1; // enable interrupt
if ( current_back_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)
active_dma_buffer_output_count = 0;
while (!active_dma_buffer_output_count) {}
//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];
@ -633,17 +577,15 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default)
} else { } else {
_dmadesc_b[_dmadesc_last].qe.stqe_next = &_dmadesc_a[0]; _dmadesc_b[_dmadesc_last].qe.stqe_next = &_dmadesc_a[0];
active_dma_buffer_output_count = 0;
while (!active_dma_buffer_output_count) {}
_dmadesc_a[_dmadesc_last].qe.stqe_next = &_dmadesc_a[0]; _dmadesc_a[_dmadesc_last].qe.stqe_next = &_dmadesc_a[0];
} }
current_back_buffer_id ^= 1;
// Disable intterupt previousBufferFree = false;
_dev->int_ena.out_eof = 0; while (i2s_parallel_is_previous_buffer_free() == false) {}
} // end flip } // end flip

View file

@ -49,9 +49,8 @@ Contributors:
#define DMA_MAX (4096-4) #define DMA_MAX (4096-4)
#ifndef ESP32_I2S_DEVICE // DO NOT CHANGE
#define ESP32_I2S_DEVICE I2S_NUM_0 #define ESP32_I2S_DEVICE I2S_NUM_1
#endif
// The type used for this SoC // The type used for this SoC
#define HUB75_DMA_DESCRIPTOR_T lldesc_t #define HUB75_DMA_DESCRIPTOR_T lldesc_t
@ -119,7 +118,7 @@ i2s_dev_t* getDev();
void dma_transfer_start(); void dma_transfer_start();
void dma_transfer_stop(); void dma_transfer_stop();
void flip_dma_output_buffer(int &current_back_buffer_id); void flip_dma_output_buffer(int buffer_id);
private: private:

View file

@ -441,12 +441,12 @@
} // end } // end
void Bus_Parallel16::flip_dma_output_buffer(int &current_back_buffer_id) void Bus_Parallel16::flip_dma_output_buffer(int back_buffer_id)
{ {
// if ( _double_dma_buffer == false) return; // if ( _double_dma_buffer == false) return;
if ( current_back_buffer_id == 1) // change across to everything 'b'' if ( back_buffer_id == 1) // change across to everything 'b''
{ {
_dmadesc_a[_dmadesc_count-1].next = (dma_descriptor_t *) &_dmadesc_b[0]; _dmadesc_a[_dmadesc_count-1].next = (dma_descriptor_t *) &_dmadesc_b[0];
_dmadesc_b[_dmadesc_count-1].next = (dma_descriptor_t *) &_dmadesc_b[0]; _dmadesc_b[_dmadesc_count-1].next = (dma_descriptor_t *) &_dmadesc_b[0];
@ -457,7 +457,7 @@
_dmadesc_a[_dmadesc_count-1].next = (dma_descriptor_t *) &_dmadesc_a[0]; _dmadesc_a[_dmadesc_count-1].next = (dma_descriptor_t *) &_dmadesc_a[0];
} }
current_back_buffer_id ^= 1; //current_back_buffer_id ^= 1;
} // end flip } // end flip

View file

@ -147,7 +147,7 @@
void dma_transfer_start(); void dma_transfer_start();
void dma_transfer_stop(); void dma_transfer_stop();
void flip_dma_output_buffer(int &current_back_buffer_id); void flip_dma_output_buffer(int back_buffer_id);
private: private: