#include "ESP32-RGB64x32MatrixPanel-I2S-DMA.h" // Credits: Louis Beaudoin // and Sprite_TM: https://www.esp32.com/viewtopic.php?f=17&t=3188 and https://www.esp32.com/viewtopic.php?f=13&t=3256 void RGB64x32MatrixPanel_I2S_DMA::configureDMA() { // calculate the lowest LSBMSB_TRANSITION_BIT value that will fit in memory int numDescriptorsPerRow; lsbMsbTransitionBit = 0; while(1) { numDescriptorsPerRow = 1; for(int i=lsbMsbTransitionBit + 1; i heap_caps_get_largest_free_block(MALLOC_CAP_DMA)){ assert("Not enough RAM for SmartMatrix descriptors"); Serial.printf("Not enough RAM for SmartMatrix descriptors\r\n"); return; } Serial.printf("Raised lsbMsbTransitionBit to %d/%d to fit in RAM\r\n", lsbMsbTransitionBit, COLOR_DEPTH_BITS - 1); // calculate the lowest LSBMSB_TRANSITION_BIT value that will fit in memory that will meet or exceed the configured refresh rate while(1) { int psPerClock = 1000000000000UL/ESP32_I2S_CLOCK_SPEED; int nsPerLatch = ((PIXELS_PER_LATCH + CLKS_DURING_LATCH) * psPerClock) / 1000; Serial.printf("ns per latch: %d: \r\n", nsPerLatch); // add time to shift out LSBs + LSB-MSB transition bit - this ignores fractions... int nsPerRow = COLOR_DEPTH_BITS * nsPerLatch; // add time to shift out MSBs for(int i=lsbMsbTransitionBit + 1; i 200) // HACK Hard Coded: Minimum frame rate of 160 break; if(lsbMsbTransitionBit < COLOR_DEPTH_BITS - 1) lsbMsbTransitionBit++; else break; } Serial.printf("Raised lsbMsbTransitionBit to %d/%d to meet minimum refresh rate\r\n", lsbMsbTransitionBit, COLOR_DEPTH_BITS - 1); // TODO: completely fill buffer with data before enabling DMA - can't do this now, lsbMsbTransition bit isn't set in the calc class - also this call will probably have no effect as matrixCalcDivider will skip the first call //matrixCalcCallback(); // lsbMsbTransition Bit is now finalized - redo descriptor count in case it changed to hit min refresh rate numDescriptorsPerRow = 1; for(int i=lsbMsbTransitionBit + 1; iclkspeed_hz + 1), must result in >=2. Acceptable values 26.67MHz, 20MHz, 16MHz, 13.34MHz... .bits=MATRIX_I2S_MODE, //MATRIX_I2S_MODE, .bufa=0, .bufb=0, desccount, desccount, dmadesc_a, dmadesc_b }; //Setup I2S i2s_parallel_setup_without_malloc(&I2S1, &cfg); Serial.printf("I2S setup done.\n"); // Just os we know dma_configuration_success = true; } // end initMatrixDMABuff void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t y_coord, uint8_t red, uint8_t green, uint8_t blue) { if ( !dma_configuration_success) assert("DMA configuration in begin() not performed or completed successfully."); // Need to check that the co-ordinate is within range, or it'll break everything big time. if ( x_coord < 0 || y_coord < 0 || x_coord > MATRIX_WIDTH || y_coord > MATRIX_HEIGHT) { return; } // What half of the HUB75 panel are we painting too? bool paint_top_half = true; if ( y_coord > ROWS_PER_FRAME-1) // co-ords start at zero, y_coord = 15 = 16 (rows per frame) { y_coord -= ROWS_PER_FRAME; // if it's 16, subtract 16. Array position 0 again. paint_top_half = false; } for(int color_depth_idx=0; color_depth_idx lsbMsbTransitionBit || !color_depth_idx) && ((x_coord) >= brightness)) v|=BIT_OE; // 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(color_depth_idx && color_depth_idx <= lsbMsbTransitionBit) { // divide brightness in half for each bit below lsbMsbTransitionBit int lsbBrightness = brightness >> (lsbMsbTransitionBit - color_depth_idx + 1); // if((x_coord) >= lsbBrightness) v|=BIT_OE; } // need to turn off OE one clock before latch, otherwise can get ghosting if((x_coord)==PIXELS_PER_LATCH-1) v|=BIT_OE; /* When using the Adafruit drawPixel, we only have one pixel co-ordinate and color to draw (duh) * so we can't paint a top and bottom half (or whatever row split the panel is) at the same time. * Need to be smart and check the DMA buffer to see what the other half thinks (pun intended) * and persis this when we refresh. * * The DMA buffer order has also been reversed (fer to the last code in this function) * so we have to check for this and check ther correct possition of the MATRIX_DATA_STORAGE_TYPE * data. */ int16_t tmp_x_coord = x_coord; if(x_coord%2) { tmp_x_coord -= 1; } else { tmp_x_coord += 1; } // end reordering if (paint_top_half) { // Need to copy what the RGB status is for the bottom pixels // Set the color of the pixel of interest if (green & mask) v|=BIT_G1; if (blue & mask) v|=BIT_B1; if (red & mask) v|=BIT_R1; // Persist what was painted to the other half of the frame equiv. pixel if (p->data[tmp_x_coord] & BIT_R2) v|=BIT_R2; if (p->data[tmp_x_coord] & BIT_G2) v|=BIT_G2; if (p->data[tmp_x_coord] & BIT_B2) v|=BIT_B2; } else { // Do it the other way around // Color to set if (red & mask) v|=BIT_R2; if (green & mask) v|=BIT_G2; if (blue & mask) v|=BIT_B2; // Copy if (p->data[tmp_x_coord] & BIT_R1) v|=BIT_R1; if (p->data[tmp_x_coord] & BIT_G1) v|=BIT_G1; if (p->data[tmp_x_coord] & BIT_B1) v|=BIT_B1; } // paint // 16 bit parallel mode //Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering if(x_coord%2){ p->data[(x_coord)-1] = v; } else { p->data[(x_coord)+1] = v; } // end reordering } // color depth loop (8) //Show our work! i2s_parallel_flip_to_buffer(&I2S1, backbuf_id); //swapBuffer(); } // updateDMABuffer /* void RGB64x32MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint16_t x, uint16_t y, uint8_t red, uint8_t green, uint8_t blue) { if ( !dma_configuration_success) assert("DMA configuration in begin() not performed or completed successfully."); // Pixel index sanity checking. int16_t idx = x + y * MATRIX_WIDTH; if (idx < 0 || idx >= MATRIX_HEIGHT * MATRIX_WIDTH) { //Serial.printf("Provided coordinates out of range"); return; } for (unsigned int y=0; y lsbMsbTransitionBit || !j) && ((i+k) >= brightness)) v|=BIT_OE; // 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(j && j <= lsbMsbTransitionBit) { // divide brightness in half for each bit below lsbMsbTransitionBit int lsbBrightness = brightness >> (lsbMsbTransitionBit - j + 1); if((i+k) >= lsbBrightness) v|=BIT_OE; } // need to turn off OE one clock before latch, otherwise can get ghosting if((i+k)==PIXELS_PER_LATCH-1) v|=BIT_OE; //#if 0 // // int c1=getpixel(pix, k, y); // int c2=getpixel(pix, k, y+(MATRIX_HEIGHT/2)); // // if (c1 & (mask<<16)) v|=BIT_R1; // if (c1 & (mask<<8)) v|=BIT_G1; // if (c1 & (mask<<0)) v|=BIT_B1; // if (c2 & (mask<<16)) v|=BIT_R2; // if ( c2 & (mask<<8)) v|=BIT_G2; // if (c2 & (mask<<0)) v|=BIT_B2; // //#else struct rgb24 c1( 255,0,0); struct rgb24 c2 = { 255,255,255 }; // struct rgb24 c2 = { 0,0,255 }; if (c1.red & mask) v|=BIT_R1; if (c1.green & mask) v|=BIT_G1; if (c1.blue & mask) v|=BIT_B1; if (c2.red & mask) v|=BIT_R2; if (c2.green & mask) v|=BIT_G2; if (c2.blue & mask) v|=BIT_B2; //#endif // 16 bit parallel mode //Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering if(k%2){ p->data[(i+k)-1] = v; } else { p->data[(i+k)+1] = v; } // end reordering } // end for MATRIX_WIDTH i += MATRIX_WIDTH; } // end pixels per latch loop (64) } // color depth loop (8) } // end half matrix length //Show our work! i2s_parallel_flip_to_buffer(&I2S1, backbuf_id); swapBuffer(); } // updateDMABuffer */