Reduce #338
This commit is contained in:
parent
9308b59445
commit
9d36abeeaf
6 changed files with 1299 additions and 1332 deletions
|
@ -10,7 +10,10 @@
|
|||
* 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.
|
||||
*/
|
||||
#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
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
||||
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 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)
|
||||
dma_buff.rowBits.reserve(ROWS_PER_FRAME);
|
||||
|
||||
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);
|
||||
|
@ -75,8 +80,9 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
|
|||
}
|
||||
|
||||
allocated_fb_memory += ptr->size();
|
||||
dma_buff.rowBits.emplace_back(ptr); // save new rowBitStruct into rows vector
|
||||
++dma_buff.rows;
|
||||
frame_buffer[fb].rowBits.emplace_back(ptr); // save new rowBitStruct into rows vector
|
||||
++frame_buffer[fb].rows;
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
while(1) {
|
||||
while (1)
|
||||
{
|
||||
int psPerClock = 1000000000000UL / m_cfg.i2sspeed;
|
||||
int nsPerLatch = ((PIXELS_PER_ROW + CLKS_DURING_LATCH) * psPerClock) / 1000;
|
||||
|
||||
|
@ -120,13 +127,13 @@ 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.
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -134,7 +141,7 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
|
|||
|
||||
// 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 ( 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);
|
||||
|
@ -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.
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* 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;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -163,8 +174,6 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
|
|||
|
||||
} // end allocateDMAmemory()
|
||||
|
||||
|
||||
|
||||
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.
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
// 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)
|
||||
{
|
||||
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++;
|
||||
|
||||
// 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++)
|
||||
{
|
||||
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) {
|
||||
dma_bus.create_dma_desc_link(dma_buff.rowBits[row]->getDataPtr(cd, 1), dma_buff.rowBits[row]->size(num_dma_payload_colour_depths), true);
|
||||
if (m_cfg.double_buff)
|
||||
{
|
||||
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++;
|
||||
|
@ -212,7 +223,6 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
|
|||
} // additional linked list items
|
||||
} // row depth struct
|
||||
|
||||
|
||||
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
|
||||
|
@ -221,10 +231,11 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
|
|||
|
||||
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) {
|
||||
dma_bus.create_dma_desc_link(dma_buff.rowBits[row]->getDataPtr(i, 1), dma_buff.rowBits[row]->size(m_cfg.getPixelColorDepthBits() - i), true );
|
||||
if (m_cfg.double_buff)
|
||||
{
|
||||
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++;
|
||||
|
@ -278,7 +289,6 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
|
|||
|
||||
} // 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.
|
||||
* 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.
|
||||
|
@ -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)
|
||||
{
|
||||
if ( !initialized ) return;
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
/* 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)
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -338,10 +350,10 @@ uint16_t red16, green16, blue16;
|
|||
*/
|
||||
x_coord = ESP32_TX_FIFO_POSITION_ADJUST(x_coord);
|
||||
|
||||
|
||||
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;
|
||||
_colourbitclear = BITMASK_RGB2_CLEAR;
|
||||
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)
|
||||
uint8_t colour_depth_idx = m_cfg.getPixelColorDepthBits();
|
||||
do {
|
||||
do
|
||||
{
|
||||
--colour_depth_idx;
|
||||
|
||||
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 <<= _colourbitoffset; // shift colour bits to the required position
|
||||
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
// 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] |= 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)
|
||||
} // updateMatrixDMABuffer (specific co-ords change)
|
||||
|
||||
|
||||
/* Update the entire buffer with a single specific colour - quicker */
|
||||
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/ */
|
||||
uint16_t red16, green16, blue16;
|
||||
|
@ -420,21 +431,22 @@ uint16_t red16, green16, blue16;
|
|||
// Serial.printf("Fill with: 0x%#06x\n", RGB_output_bits);
|
||||
|
||||
// iterate rows
|
||||
int matrix_frame_parallel_row = dma_buff.rowBits.size();
|
||||
do {
|
||||
int matrix_frame_parallel_row = fb->rowBits.size();
|
||||
do
|
||||
{
|
||||
--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);
|
||||
|
||||
// iterate pixels in a row
|
||||
int x_coord=dma_buff.rowBits[matrix_frame_parallel_row]->width;
|
||||
do {
|
||||
int x_coord = fb->rowBits[matrix_frame_parallel_row]->width;
|
||||
do
|
||||
{
|
||||
--x_coord;
|
||||
p[x_coord] &= BITMASK_RGB12_CLEAR; // reset colour bits
|
||||
p[x_coord] |= RGB_output_bits; // set new colour bits
|
||||
|
||||
|
||||
#if defined(SPIRAM_DMA_BUFFER)
|
||||
Cache_WriteBack_Addr((uint32_t)&p[x_coord], sizeof(ESP32_I2S_DMA_STORAGE_TYPE));
|
||||
#endif
|
||||
|
@ -453,63 +465,74 @@ uint16_t red16, green16, blue16;
|
|||
* 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!
|
||||
*/
|
||||
void MatrixPanel_I2S_DMA::clearFrameBuffer(bool _buff_id){
|
||||
void MatrixPanel_I2S_DMA::clearFrameBuffer(bool _buff_id)
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
// we start with iterating all rows in dma_buff structure
|
||||
int row_idx = dma_buff.rowBits.size();
|
||||
do {
|
||||
int row_idx = fb->rowBits.size();
|
||||
do
|
||||
{
|
||||
--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;
|
||||
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
|
||||
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);
|
||||
|
||||
// fill all x_pixels except colour_index[0] (LSB) ones, this also clears all colour data to 0's black
|
||||
do {
|
||||
do
|
||||
{
|
||||
--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
|
||||
// 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
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
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
|
||||
// previous row while we pump in LSB's for a new row
|
||||
abcde = ((ESP32_I2S_DMA_STORAGE_TYPE)row_idx - 1) << BITS_ADDR_OFFSET;
|
||||
do {
|
||||
do
|
||||
{
|
||||
--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
|
||||
// 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
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
row[ESP32_TX_FIFO_POSITION_ADJUST(x_pixel)] = abcde;
|
||||
}
|
||||
|
||||
} while (x_pixel);
|
||||
|
||||
|
||||
// modifications here for row shift register type SM5266P
|
||||
// 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 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;
|
||||
do{
|
||||
do
|
||||
{
|
||||
serialCount--;
|
||||
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
|
||||
|
@ -517,153 +540,67 @@ void MatrixPanel_I2S_DMA::clearFrameBuffer(bool _buff_id){
|
|||
} while (serialCount);
|
||||
} // end SM5266P
|
||||
|
||||
|
||||
// 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...
|
||||
uint8_t colouridx = dma_buff.rowBits[row_idx]->colour_depth;
|
||||
do {
|
||||
uint8_t colouridx = fb->rowBits[row_idx]->colour_depth;
|
||||
do
|
||||
{
|
||||
--colouridx;
|
||||
|
||||
// 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)
|
||||
|
||||
// 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 {
|
||||
do
|
||||
{
|
||||
--_blank;
|
||||
|
||||
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(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 - 1)] |= BIT_OE; // disable output
|
||||
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 (colouridx);
|
||||
|
||||
|
||||
|
||||
#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
|
||||
|
||||
|
||||
} 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
|
||||
* @param brt - brightness level from 0 to 255 - NOT MATRIX_WIDTH
|
||||
* @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)
|
||||
return;
|
||||
|
||||
uint8_t _blank = m_cfg.latch_blanking; // don't want to inadvertantly blast over this
|
||||
uint8_t _depth = dma_buff.rowBits[0]->colour_depth;
|
||||
uint16_t _width = dma_buff.rowBits[0]->width;
|
||||
uint8_t _depth = fb->rowBits[0]->colour_depth;
|
||||
uint16_t _width = fb->rowBits[0]->width;
|
||||
|
||||
// start with iterating all rows in dma_buff structure
|
||||
int row_idx = dma_buff.rowBits.size();
|
||||
do {
|
||||
int row_idx = fb->rowBits.size();
|
||||
do
|
||||
{
|
||||
--row_idx;
|
||||
|
||||
// let's set OE control bits for specific pixels in each color_index subrows
|
||||
uint8_t colouridx = _depth;
|
||||
do {
|
||||
do
|
||||
{
|
||||
--colouridx;
|
||||
|
||||
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);
|
||||
|
||||
// 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
|
||||
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 = _width;
|
||||
do {
|
||||
do
|
||||
{
|
||||
--x_coord;
|
||||
|
||||
// (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
|
||||
#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)) ;
|
||||
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) * ((fb->rowBits[row_idx]->width * fb->rowBits[row_idx]->colour_depth) - 1));
|
||||
#endif
|
||||
} while (row_idx);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
if(initialized) return true;
|
||||
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;
|
||||
// RGB
|
||||
m_cfg.gpio.r1 = r1; 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;
|
||||
m_cfg.gpio.r1 = r1;
|
||||
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
|
||||
m_cfg.gpio.a = a; m_cfg.gpio.b = b; m_cfg.gpio.c = c;
|
||||
m_cfg.gpio.d = d; m_cfg.gpio.e = e;
|
||||
m_cfg.gpio.a = a;
|
||||
m_cfg.gpio.b = b;
|
||||
m_cfg.gpio.c = c;
|
||||
m_cfg.gpio.d = d;
|
||||
m_cfg.gpio.e = e;
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
bool MatrixPanel_I2S_DMA::begin(const HUB75_I2S_CFG& cfg){
|
||||
if(initialized) return true;
|
||||
bool MatrixPanel_I2S_DMA::begin(const HUB75_I2S_CFG &cfg)
|
||||
{
|
||||
if (initialized)
|
||||
return true;
|
||||
|
||||
if(!setCfg(cfg)) return false;
|
||||
if (!setCfg(cfg))
|
||||
return false;
|
||||
|
||||
return begin();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief - Sets how many clock cycles to blank OE before/after LAT signal change
|
||||
* @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
|
||||
* @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)
|
||||
pulses = MAX_LAT_BLANKING;
|
||||
|
||||
|
@ -759,7 +708,6 @@ uint8_t MatrixPanel_I2S_DMA::setLatBlanking(uint8_t pulses){
|
|||
return m_cfg.latch_blanking;
|
||||
}
|
||||
|
||||
|
||||
#ifndef NO_FAST_FUNCTIONS
|
||||
/**
|
||||
* @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 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)
|
||||
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;
|
||||
x_coord = x_coord < 0 ? 0 : x_coord;
|
||||
|
||||
|
||||
l = ((x_coord + l) >= PIXELS_PER_ROW) ? (PIXELS_PER_ROW - x_coord) : l;
|
||||
|
||||
// if (x_coord+l > PIXELS_PER_ROW)
|
||||
|
@ -798,7 +746,8 @@ uint16_t red16, green16, blue16;
|
|||
|
||||
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;
|
||||
_colourbitclear = BITMASK_RGB2_CLEAR;
|
||||
y_coord -= ROWS_PER_FRAME;
|
||||
|
@ -806,7 +755,8 @@ uint16_t red16, green16, blue16;
|
|||
|
||||
// Iterating through colour depth bits (8 iterations)
|
||||
uint8_t colour_depth_idx = m_cfg.getPixelColorDepthBits();
|
||||
do {
|
||||
do
|
||||
{
|
||||
--colour_depth_idx;
|
||||
|
||||
// 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,
|
||||
// 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 :(
|
||||
// ESP32_I2S_DMA_STORAGE_TYPE *p = getRowDataPtr(y_coord, colour_depth_idx, back_buffer_id);
|
||||
|
||||
int16_t _l = l;
|
||||
do { // iterate pixels in a row
|
||||
do
|
||||
{ // iterate pixels in a row
|
||||
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)
|
||||
} // hlineDMA()
|
||||
|
||||
|
||||
/**
|
||||
* @brief - update DMA buff drawing vertical line at specified coordinates
|
||||
* @param x_coord - line start coordinate x
|
||||
|
@ -863,7 +813,8 @@ uint16_t red16, green16, blue16;
|
|||
* @param l - line length
|
||||
* @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)
|
||||
return;
|
||||
|
||||
|
@ -899,7 +850,8 @@ uint16_t red16, green16, blue16;
|
|||
x_coord = ESP32_TX_FIFO_POSITION_ADJUST(x_coord);
|
||||
|
||||
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;
|
||||
|
||||
// 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;
|
||||
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;
|
||||
_colourbitclear = BITMASK_RGB2_CLEAR;
|
||||
RGB_output_bits <<= BITS_RGB2_OFFSET;
|
||||
|
@ -934,7 +888,7 @@ uint16_t red16, green16, blue16;
|
|||
// 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 = 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] |= 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)
|
||||
} // vlineDMA()
|
||||
|
||||
|
||||
/**
|
||||
* @brief - update DMA buff drawing a rectangular at specified coordinates
|
||||
* 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 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
|
||||
// so will use it only for tall rects with h >2w
|
||||
if (h>2*w){
|
||||
if (h > 2 * w)
|
||||
{
|
||||
// draw using v-lines
|
||||
do {
|
||||
do
|
||||
{
|
||||
--w;
|
||||
vlineDMA(x + w, y, h, r, g, b);
|
||||
} while (w);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// draw using h-lines
|
||||
do {
|
||||
do
|
||||
{
|
||||
--h;
|
||||
hlineDMA(x, y + h, w, r, g, b);
|
||||
} while (h);
|
||||
|
|
|
@ -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
|
||||
#endif
|
||||
|
||||
|
||||
// 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.
|
||||
|
||||
|
@ -129,7 +128,8 @@
|
|||
/** @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
|
||||
*/
|
||||
struct rowBitStruct {
|
||||
struct rowBitStruct
|
||||
{
|
||||
const size_t width;
|
||||
const uint8_t colour_depth;
|
||||
const bool double_buff;
|
||||
|
@ -143,24 +143,34 @@ struct rowBitStruct {
|
|||
* 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
|
||||
* 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
|
||||
* 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
|
||||
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_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_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);
|
||||
|
||||
// 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())
|
||||
{
|
||||
|
@ -168,15 +178,16 @@ struct rowBitStruct {
|
|||
}
|
||||
*/
|
||||
#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");
|
||||
#endif
|
||||
|
||||
}
|
||||
~rowBitStruct() { delete data; }
|
||||
};
|
||||
|
||||
|
||||
/* frameStruct
|
||||
* 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).
|
||||
|
@ -184,7 +195,8 @@ struct rowBitStruct {
|
|||
* This structure isn't actually allocated in one memory block anymore, as the library now allocates
|
||||
* 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
|
||||
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,
|
||||
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,
|
||||
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
|
||||
|
||||
/** @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.
|
||||
* All params have it's default values.
|
||||
*/
|
||||
struct HUB75_I2S_CFG {
|
||||
struct HUB75_I2S_CFG
|
||||
{
|
||||
|
||||
/**
|
||||
* Enumeration of hardware-specific chips
|
||||
* 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
|
||||
*/
|
||||
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.
|
||||
|
@ -253,7 +278,10 @@ struct HUB75_I2S_CFG {
|
|||
uint16_t chain_length;
|
||||
|
||||
// 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
|
||||
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()
|
||||
uint8_t min_refresh_rate;
|
||||
|
||||
|
||||
// struct constructor
|
||||
HUB75_I2S_CFG(
|
||||
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,
|
||||
A_PIN_DEFAULT, B_PIN_DEFAULT, C_PIN_DEFAULT, D_PIN_DEFAULT, E_PIN_DEFAULT,
|
||||
LAT_PIN_DEFAULT, OE_PIN_DEFAULT, CLK_PIN_DEFAULT},
|
||||
shift_driver _drv = SHIFTREG,
|
||||
bool _dbuff = false,
|
||||
clk_speed _i2sspeed = HZ_15M,
|
||||
shift_driver _drv = SHIFTREG, bool _dbuff = false, clk_speed _i2sspeed = HZ_15M,
|
||||
uint8_t _latblk = DEFAULT_LAT_BLANKING, // Anything > 1 seems to cause artefacts on ICS panels
|
||||
bool _clockphase = true,
|
||||
uint16_t _min_refresh_rate = 60,
|
||||
uint8_t _pixel_color_depth_bits = PIXEL_COLOR_DEPTH_BITS_DEFAULT
|
||||
) : mx_width(_w),
|
||||
mx_height(_h),
|
||||
chain_length(_chain),
|
||||
gpio(_pinmap),
|
||||
driver(_drv),
|
||||
double_buff(_dbuff),
|
||||
i2sspeed(_i2sspeed),
|
||||
latch_blanking(_latblk),
|
||||
clkphase(_clockphase),
|
||||
min_refresh_rate(_min_refresh_rate)
|
||||
bool _clockphase = true, uint16_t _min_refresh_rate = 60, uint8_t _pixel_color_depth_bits = PIXEL_COLOR_DEPTH_BITS_DEFAULT) : mx_width(_w), mx_height(_h), chain_length(_chain), gpio(_pinmap), driver(_drv), double_buff(_dbuff), i2sspeed(_i2sspeed), latch_blanking(_latblk), clkphase(_clockphase), min_refresh_rate(_min_refresh_rate)
|
||||
{
|
||||
setPixelColorDepthBits(_pixel_color_depth_bits);
|
||||
}
|
||||
|
||||
// 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)
|
||||
void setPixelColorDepthBits(uint8_t _pixel_color_depth_bits){
|
||||
if(_pixel_color_depth_bits > PIXEL_COLOR_DEPTH_BITS_MAX || _pixel_color_depth_bits < 2){
|
||||
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){
|
||||
if (_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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t getPixelColorDepthBits(){
|
||||
uint8_t getPixelColorDepthBits()
|
||||
{
|
||||
return pixel_color_depth_bits;
|
||||
}
|
||||
|
||||
|
@ -343,20 +364,20 @@ struct HUB75_I2S_CFG {
|
|||
uint8_t pixel_color_depth_bits;
|
||||
}; // end of structure HUB75_I2S_CFG
|
||||
|
||||
|
||||
|
||||
/***************************************************************************************/
|
||||
#ifdef USE_GFX_ROOT
|
||||
class MatrixPanel_I2S_DMA : public GFX {
|
||||
class MatrixPanel_I2S_DMA : public GFX
|
||||
{
|
||||
#elif !defined NO_GFX
|
||||
class MatrixPanel_I2S_DMA : public Adafruit_GFX {
|
||||
class MatrixPanel_I2S_DMA : public Adafruit_GFX
|
||||
{
|
||||
#else
|
||||
class MatrixPanel_I2S_DMA {
|
||||
class MatrixPanel_I2S_DMA
|
||||
{
|
||||
#endif
|
||||
|
||||
// ------- PUBLIC -------
|
||||
public:
|
||||
|
||||
/**
|
||||
* MatrixPanel_I2S_DMA
|
||||
*
|
||||
|
@ -369,7 +390,8 @@ class MatrixPanel_I2S_DMA {
|
|||
#elif !defined NO_GFX
|
||||
: Adafruit_GFX(MATRIX_WIDTH, MATRIX_HEIGHT)
|
||||
#endif
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
bool begin(){
|
||||
bool begin()
|
||||
{
|
||||
|
||||
if (initialized) return true; // we don't do this twice or more!
|
||||
if(!config_set) return false;
|
||||
if (initialized)
|
||||
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 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 CLK_PIN", m_cfg.gpio.clk);
|
||||
|
||||
|
||||
// initialize some specific panel drivers
|
||||
if (m_cfg.driver)
|
||||
shiftDriver(m_cfg);
|
||||
|
@ -422,8 +446,10 @@ class MatrixPanel_I2S_DMA {
|
|||
/* As DMA buffers are dynamically allocated, we must allocated in begin()
|
||||
* 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.
|
||||
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
|
||||
|
||||
if (!initialized) {
|
||||
if (!initialized)
|
||||
{
|
||||
ESP_LOGE("being()", "MatrixPanel_I2S_DMA::begin() failed!");
|
||||
}
|
||||
|
||||
return initialized;
|
||||
|
||||
}
|
||||
|
||||
// Obj destructor
|
||||
~MatrixPanel_I2S_DMA(){
|
||||
~MatrixPanel_I2S_DMA()
|
||||
{
|
||||
|
||||
dma_bus.release();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* overload for compatibility
|
||||
*/
|
||||
|
@ -469,7 +494,8 @@ class MatrixPanel_I2S_DMA {
|
|||
* @brief - override Adafruit's FastVLine
|
||||
* 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;
|
||||
color565to888(color, r, g, b);
|
||||
startWrite();
|
||||
|
@ -484,7 +510,8 @@ class MatrixPanel_I2S_DMA {
|
|||
endWrite();
|
||||
}
|
||||
// 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;
|
||||
transform(x, y, w, h);
|
||||
if (h > w)
|
||||
|
@ -497,7 +524,8 @@ class MatrixPanel_I2S_DMA {
|
|||
* @brief - override Adafruit's FastHLine
|
||||
* 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;
|
||||
color565to888(color, r, g, b);
|
||||
startWrite();
|
||||
|
@ -512,7 +540,8 @@ class MatrixPanel_I2S_DMA {
|
|||
endWrite();
|
||||
}
|
||||
// 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;
|
||||
transform(x, y, w, h);
|
||||
if (h > w)
|
||||
|
@ -525,7 +554,8 @@ class MatrixPanel_I2S_DMA {
|
|||
* @brief - override Adafruit's fillRect
|
||||
* 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;
|
||||
color565to888(color, r, g, b);
|
||||
startWrite();
|
||||
|
@ -534,7 +564,8 @@ class MatrixPanel_I2S_DMA {
|
|||
endWrite();
|
||||
}
|
||||
// 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();
|
||||
transform(x, y, w, h);
|
||||
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);
|
||||
|
||||
|
||||
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);
|
||||
|
||||
back_buffer_id ^= 1;
|
||||
|
||||
// initialized = true;
|
||||
|
||||
/*
|
||||
|
@ -593,7 +636,6 @@ class MatrixPanel_I2S_DMA {
|
|||
// Wait before we allow any writing to the buffer. Stop flicker.
|
||||
while(i2s_parallel_is_previous_buffer_free() == false) { }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -610,11 +652,10 @@ class MatrixPanel_I2S_DMA {
|
|||
brightness = b;
|
||||
brtCtrlOEv2(b, 0);
|
||||
|
||||
if (m_cfg.double_buff) {
|
||||
if (m_cfg.double_buff)
|
||||
{
|
||||
brtCtrlOEv2(b, 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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief - Sets how many clock cycles to blank OE before/after LAT signal change
|
||||
* @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; };
|
||||
|
||||
inline bool setCfg(const HUB75_I2S_CFG& cfg){
|
||||
if(initialized) return false;
|
||||
inline bool setCfg(const HUB75_I2S_CFG &cfg)
|
||||
{
|
||||
if (initialized)
|
||||
return false;
|
||||
|
||||
m_cfg = cfg;
|
||||
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.
|
||||
*/
|
||||
void stopDMAoutput() {
|
||||
void stopDMAoutput()
|
||||
{
|
||||
resetbuffers();
|
||||
// i2s_parallel_stop_dma(ESP32_I2S_DEVICE);
|
||||
dma_bus.dma_transfer_stop();
|
||||
}
|
||||
|
||||
void startWrite() {
|
||||
void startWrite()
|
||||
{
|
||||
// ESP_LOGI("TAG", "startWrite() called");
|
||||
active_gfx_writes++;
|
||||
}
|
||||
|
||||
|
||||
void endWrite() {
|
||||
void endWrite()
|
||||
{
|
||||
active_gfx_writes--;
|
||||
}
|
||||
|
||||
// ------- PROTECTED -------
|
||||
// those might be useful for child classes, like VirtualMatrixPanel
|
||||
protected:
|
||||
|
||||
/**
|
||||
* @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.
|
||||
|
@ -725,19 +768,20 @@ class MatrixPanel_I2S_DMA {
|
|||
/**
|
||||
* wipes DMA buffer(s) and reset all colour/service bits
|
||||
*/
|
||||
inline void resetbuffers(){
|
||||
inline void resetbuffers()
|
||||
{
|
||||
|
||||
clearFrameBuffer();
|
||||
brtCtrlOEv2(brightness, 0);
|
||||
// flipDMABuffer();
|
||||
fb = &frame_buffer[0];
|
||||
|
||||
if (m_cfg.double_buff){
|
||||
clearFrameBuffer(1);
|
||||
brtCtrlOEv2(brightness, 1);
|
||||
clearFrameBuffer(0); // buffer ID is not used
|
||||
brtCtrlOEv2(brightness, 0); // buffer ID is not used
|
||||
|
||||
fb = &frame_buffer[1];
|
||||
clearFrameBuffer(1); // buffer ID is not used
|
||||
brtCtrlOEv2(brightness, 1); // buffer ID is not used
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifndef NO_FAST_FUNCTIONS
|
||||
/**
|
||||
* @brief - update DMA buff drawing horizontal line at specified coordinates
|
||||
|
@ -771,7 +815,6 @@ class MatrixPanel_I2S_DMA {
|
|||
|
||||
// ------- PRIVATE -------
|
||||
private:
|
||||
|
||||
/* Calculate the memory available for DMA use, do some other stuff, and allocate accordingly */
|
||||
bool allocateDMAmemory();
|
||||
|
||||
|
@ -810,28 +853,47 @@ class MatrixPanel_I2S_DMA {
|
|||
* @param w - rectangular width
|
||||
* @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
|
||||
int16_t t;
|
||||
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 3: t = y; y = _width - 1 - x - ( w - 1 ); x = t; t = h; h = w; w = t; return;
|
||||
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 3:
|
||||
t = y;
|
||||
y = _width - 1 - x - (w - 1);
|
||||
x = t;
|
||||
t = h;
|
||||
h = w;
|
||||
w = t;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
/**
|
||||
* Contains the resulting refresh rate (scan rate) that will be achieved
|
||||
* based on the i2sspeed, colour depth and min_refresh_rate requested.
|
||||
*/
|
||||
int calculated_refresh_rate = 0;
|
||||
|
||||
protected:
|
||||
Bus_Parallel16 dma_bus;
|
||||
private:
|
||||
|
||||
private:
|
||||
// Matrix i2s settings
|
||||
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.
|
||||
* 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
|
||||
int desccount = 0;
|
||||
|
@ -878,7 +943,8 @@ class MatrixPanel_I2S_DMA {
|
|||
* @param uint16_t colour - RGB565 input colour
|
||||
* @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;
|
||||
g = ((((color >> 5) & 0x3F) * 259) + 33) >> 6;
|
||||
b = (((color & 0x1F) * 527) + 23) >> 6;
|
||||
|
@ -929,19 +995,21 @@ inline void MatrixPanel_I2S_DMA::fillScreen(CRGB color)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Pass 8-bit (each) R,G,B, get back 16-bit packed colour
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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.
|
||||
// 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;
|
||||
for (i = 0; i < rows; i++) {
|
||||
for (j = 0; j < cols; j++) {
|
||||
for (i = 0; i < rows; i++)
|
||||
{
|
||||
for (j = 0; j < cols; j++)
|
||||
{
|
||||
drawPixel(x + j, y + i, (uint16_t)ico[i * cols + j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
|
|
|
@ -28,54 +28,28 @@ Modified heavily for the ESP32 HUB75 DMA library by:
|
|||
#include <driver/periph_ctrl.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_log.h>
|
||||
|
||||
// Get CPU freq function.
|
||||
#include <soc/rtc.h>
|
||||
|
||||
/*
|
||||
|
||||
callback shiftCompleteCallback;
|
||||
void setShiftCompleteCallback(callback f) {
|
||||
shiftCompleteCallback = f;
|
||||
}
|
||||
|
||||
volatile int previousBufferOutputLoopCount = 0;
|
||||
volatile bool previousBufferFree = true;
|
||||
|
||||
static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default)
|
||||
|
||||
SET_PERI_REG_BITS(I2S_INT_CLR_REG(ESP32_I2S_DEVICE), I2S_OUT_EOF_INT_CLR_V, 1, I2S_OUT_EOF_INT_CLR_S);
|
||||
// Todo: handle IS20? (this is hard coded for I2S1 only)
|
||||
static void IRAM_ATTR i2s_isr(void* arg) {
|
||||
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;
|
||||
|
||||
|
||||
|
||||
} // 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
|
||||
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));
|
||||
|
||||
// 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
|
||||
#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
|
||||
* that can be used for I2S0/I2S1 created interrupts.
|
||||
*/
|
||||
if (_double_dma_buffer) {
|
||||
|
||||
// Get ISR setup
|
||||
esp_err_t err = esp_intr_alloc(irq_source,
|
||||
(int)(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1),
|
||||
irq_hndlr, NULL, NULL);
|
||||
// setup I2S Interrupt
|
||||
SET_PERI_REG_BITS(I2S_INT_ENA_REG(1), I2S_OUT_EOF_INT_ENA_V, 1, I2S_OUT_EOF_INT_ENA_S);
|
||||
// allocate a level 1 intterupt: lowest priority, as ISR isn't urgent and may take a long time to complete
|
||||
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)
|
||||
|
@ -611,21 +560,16 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default)
|
|||
} // end
|
||||
|
||||
|
||||
void Bus_Parallel16::flip_dma_output_buffer(int ¤t_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)
|
||||
// "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)
|
||||
|
||||
active_dma_buffer_output_count = 0;
|
||||
while (!active_dma_buffer_output_count) {}
|
||||
|
||||
//fix _dmadesc_ loop issue #407
|
||||
//need to connect the up comming _dmadesc_ not the old one
|
||||
_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 {
|
||||
|
||||
_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];
|
||||
|
||||
}
|
||||
current_back_buffer_id ^= 1;
|
||||
|
||||
// Disable intterupt
|
||||
_dev->int_ena.out_eof = 0;
|
||||
previousBufferFree = false;
|
||||
while (i2s_parallel_is_previous_buffer_free() == false) {}
|
||||
|
||||
|
||||
|
||||
|
||||
} // end flip
|
||||
|
||||
|
|
|
@ -49,9 +49,8 @@ Contributors:
|
|||
|
||||
#define DMA_MAX (4096-4)
|
||||
|
||||
#ifndef ESP32_I2S_DEVICE
|
||||
#define ESP32_I2S_DEVICE I2S_NUM_0
|
||||
#endif
|
||||
// DO NOT CHANGE
|
||||
#define ESP32_I2S_DEVICE I2S_NUM_1
|
||||
|
||||
// The type used for this SoC
|
||||
#define HUB75_DMA_DESCRIPTOR_T lldesc_t
|
||||
|
@ -119,7 +118,7 @@ i2s_dev_t* getDev();
|
|||
void dma_transfer_start();
|
||||
void dma_transfer_stop();
|
||||
|
||||
void flip_dma_output_buffer(int ¤t_back_buffer_id);
|
||||
void flip_dma_output_buffer(int buffer_id);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -441,12 +441,12 @@
|
|||
} // end
|
||||
|
||||
|
||||
void Bus_Parallel16::flip_dma_output_buffer(int ¤t_back_buffer_id)
|
||||
void Bus_Parallel16::flip_dma_output_buffer(int back_buffer_id)
|
||||
{
|
||||
|
||||
// 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_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];
|
||||
}
|
||||
|
||||
current_back_buffer_id ^= 1;
|
||||
//current_back_buffer_id ^= 1;
|
||||
|
||||
|
||||
} // end flip
|
||||
|
|
|
@ -147,7 +147,7 @@
|
|||
void dma_transfer_start();
|
||||
void dma_transfer_stop();
|
||||
|
||||
void flip_dma_output_buffer(int ¤t_back_buffer_id);
|
||||
void flip_dma_output_buffer(int back_buffer_id);
|
||||
|
||||
private:
|
||||
|
||||
|
|
Loading…
Reference in a new issue