diff options
| author | mrfaptastic <12006953+mrfaptastic@users.noreply.github.com> | 2020-12-07 18:19:31 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-12-07 18:19:31 +0000 |
| commit | 3cda03ab83cc9309bf237f37c03b9e09a0cc2501 (patch) | |
| tree | 9253bfad7645761e6cbcd486d3578e0331a33b07 | |
| parent | c72dbb136056e6a2176005a9ef6d4220dedaf182 (diff) | |
| parent | fd4694046449ef27dc73e055af654a650db9e7e5 (diff) | |
Merge pull request #44 from vortigont/brtctrl
FastMode with brightness control + various other code optimisations.
| -rw-r--r-- | ESP32-HUB75-MatrixPanel-I2S-DMA.cpp | 468 | ||||
| -rw-r--r-- | ESP32-HUB75-MatrixPanel-I2S-DMA.h | 59 | ||||
| -rw-r--r-- | FM6126A.md | 68 | ||||
| -rw-r--r-- | examples/AuroraDemo/AuroraDemo.ino | 13 | ||||
| -rw-r--r-- | examples/AuroraDemo/PatternSwirl.h | 2 | ||||
| -rw-r--r-- | examples/FM6126Panel/FM6126Panel.ino | 36 | ||||
| -rw-r--r-- | fillrate.md | 25 |
7 files changed, 338 insertions, 333 deletions
diff --git a/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp b/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp index 586092c..97a9386 100644 --- a/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp +++ b/ESP32-HUB75-MatrixPanel-I2S-DMA.cpp @@ -112,7 +112,7 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory() matrix_row_framebuffer_malloc[malloc_num] = (rowColorDepthStruct *)heap_caps_malloc( (sizeof(rowColorDepthStruct) * _num_frame_buffers) , MALLOC_CAP_DMA); // If the ESP crashes here, then we must have a horribly fragmented memory space, or trying to allocate a ludicrous resolution. #if SERIAL_DEBUG - Serial.printf("Malloc'ing %d bytes of memory @ address %d for frame row %d.\r\n", (sizeof(rowColorDepthStruct) * _num_frame_buffers), matrix_row_framebuffer_malloc[malloc_num], malloc_num); + Serial.printf("Malloc'ing %d bytes of memory @ address %ud for frame row %d.\r\n", (sizeof(rowColorDepthStruct) * _num_frame_buffers), (unsigned int)matrix_row_framebuffer_malloc[malloc_num], malloc_num); #endif if ( matrix_row_framebuffer_malloc[malloc_num] == NULL ) { Serial.printf("ERROR: Couldn't malloc matrix_row_framebuffer %d! Critical fail.\r\n", malloc_num); @@ -438,108 +438,33 @@ void MatrixPanel_I2S_DMA::configureDMA(int r1_pin, int g1_pin, int b1_pin, int * * Note: If you change the brightness with setBrightness() you MUST then clearScreen() and repaint / flush the entire framebuffer. */ -//#define GO_FOR_SPEED 1 - -#ifdef GO_FOR_SPEED -/* Update a specific co-ordinate in the DMA buffer */ -void MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t y_coord, uint8_t red, uint8_t green, uint8_t blue) -{ - - // 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 < 0 || y_coord < 0 || x_coord >= MATRIX_WIDTH || y_coord >= MATRIX_HEIGHT) { - return; - } - - // https://ledshield.wordpress.com/2012/11/13/led-brightness-to-your-eye-gamma-correction-no/ - red = lumConvTab[red]; - green = lumConvTab[green]; - blue = lumConvTab[blue]; - - bool painting_top_frame = true; - if ( y_coord >= ROWS_PER_FRAME) // co-ords start at zero, y_coord = 15 = 16 (rows per frame) - { - y_coord -= ROWS_PER_FRAME; // Subtract the ROWS_PER_FRAME from the pixel co-ord to get the panel ROW (not really the 'y_coord' anymore) - painting_top_frame = false; - } - - // 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 - int rowBitStruct_x_coord_uint16_t_position = (x_coord % 2) ? (x_coord-1):(x_coord+1); - - // Find the memory address for the malloc for this framebuffer row. - rowColorDepthStruct *fb_row_malloc_ptr = (rowColorDepthStruct *) matrix_row_framebuffer_malloc[y_coord]; - - for(int color_depth_idx=0; color_depth_idx<PIXEL_COLOR_DEPTH_BITS; color_depth_idx++) // color depth - 8 iterations - { - uint8_t mask = (1 << color_depth_idx); // PWM bit colour mask (max 8bits per pixel colour) - - // The destination for the pixel bitstream - //rowBitStruct *p = &matrix_framebuffer_malloc_1[back_buffer_id].rowdata[y_coord].rowbits[color_depth_idx]; //matrixUpdateFrames location to write to uint16_t's - // Get the contents at this address, cast as a rowColorDepthStruct - //rowBitStruct *p = &fb_row_malloc_ptr[back_buffer_id].rowbits[color_depth_idx]; //matrixUpdateFrames location to write to uint16_t's - uint16_t &v = fb_row_malloc_ptr[back_buffer_id].rowbits[color_depth_idx].data[rowBitStruct_x_coord_uint16_t_position]; - - //int v=0; // the output bitstream - - if (painting_top_frame) // Painting to pixel in the top half of the HUB75 panel use the R1, B1 and G1 pins - { - // Set the colour of the pixel of interest - // https://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit - if (red & mask) { v|=BIT_R1; } else { v &= ~(BIT_R1); } - if (green & mask) { v|=BIT_G1; } else { v &= ~(BIT_G1); } - if (blue & mask) { v|=BIT_B1; } else { v &= ~(BIT_B1); } - } - else - { // Paint to a pixel in the bottom half - - if (red & mask) { v|=BIT_R2; } else { v &= ~(BIT_R2); } - if (green & mask) { v|=BIT_G2; } else { v &= ~(BIT_G2); } - if (blue & mask) { v|=BIT_B2; } else { v &= ~(BIT_B2); } - - } // paint - - // 16 bit parallel mode - //Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering - //p->data[rowBitStruct_x_coord_uint16_t_position] = v; - // NOTE: No need to do this as 'v' is now a reference directly to the frameStruct - - } // color depth loop (8) - -} // updateMatrixDMABuffer (specific co-ords change) - -#else /* Update a specific co-ordinate in the DMA buffer */ /* Original version were we re-create the bitstream from scratch for each x,y co-ordinate / pixel changed. Slightly slower. */ void MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t y_coord, uint8_t red, uint8_t green, uint8_t blue) { if ( !everything_OK ) { - #if SERIAL_DEBUG Serial.println("Cannot updateMatrixDMABuffer as setup failed!"); #endif - return; } - - /* LED Brightness Compensation. Because if we do a basic "red & mask" for example, - * we'll NEVER send the dimmest possible colour, due to binary skew. - - i.e. It's almost impossible for color_depth_idx of 0 to be sent out to the MATRIX unless the 'value' of a color is exactly '1' - - */ - red = lumConvTab[red]; - green = lumConvTab[green]; - blue = lumConvTab[blue]; - - /* 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 < 0 || y_coord < 0 || x_coord >= MATRIX_WIDTH || y_coord >= MATRIX_HEIGHT) { - 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 < 0 || y_coord < 0 || x_coord >= MATRIX_WIDTH || y_coord >= MATRIX_HEIGHT) { + return; + } + + /* LED Brightness Compensation. Because if we do a basic "red & mask" for example, + * we'll NEVER send the dimmest possible colour, due to binary skew. + * i.e. It's almost impossible for color_depth_idx of 0 to be sent out to the MATRIX unless the 'value' of a color is exactly '1' + * https://ledshield.wordpress.com/2012/11/13/led-brightness-to-your-eye-gamma-correction-no/ + */ + red = lumConvTab[red]; + green = lumConvTab[green]; + blue = lumConvTab[blue]; /* When using the drawPixel, we are obviously only changing the value of one x,y position, * however, the two-scan panels paint TWO lines at the same time @@ -553,66 +478,69 @@ void MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t y_coord * data. */ bool painting_top_frame = true; - if ( y_coord >= ROWS_PER_FRAME) // co-ords start at zero, y_coord = 15 = 16 (rows per frame) + if ( y_coord >= ROWS_PER_FRAME) // co-ords start at zero, y_coord = 15 => 16 (rows per frame) { y_coord -= ROWS_PER_FRAME; // Subtract the ROWS_PER_FRAME from the pixel co-ord to get the panel ROW (not really the 'y_coord' anymore) painting_top_frame = false; } - // We need to update the correct uint16_t in the rowBitStruct array, that gets sent out in parallel - int rowBitStruct_x_coord_uint16_t_position = (x_coord % 2) ? (x_coord-1):(x_coord+1); - - for(int color_depth_idx=0; color_depth_idx<PIXEL_COLOR_DEPTH_BITS; color_depth_idx++) // color depth - 8 iterations + // Find the memory address for the malloc for this framebuffer row. + rowColorDepthStruct *fb_row_malloc_ptr = (rowColorDepthStruct *) matrix_row_framebuffer_malloc[y_coord]; + + // 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 + uint16_t rowBitStruct_x_coord_uint16_t_position = (x_coord % 2) ? (x_coord-1):(x_coord+1); + + for(uint8_t color_depth_idx=0; color_depth_idx<PIXEL_COLOR_DEPTH_BITS; color_depth_idx++) // color depth - 8 iterations { - int mask = (1 << color_depth_idx); // 24 bit color + uint8_t mask = (1 << color_depth_idx); // 24 bit color - // The destination for the pixel bitstream - //rowBitStruct *p = &matrix_framebuffer_malloc_1[back_buffer_id].rowdata[y_coord].rowbits[color_depth_idx]; //matrixUpdateFrames location to write to uint16_t's - - // Find the memory address for the malloc for this framebuffer row. - rowColorDepthStruct *fb_row_malloc_ptr = (rowColorDepthStruct *) matrix_row_framebuffer_malloc[y_coord]; // Get the contents at this address, cast as a rowColorDepthStruct rowBitStruct *p = &fb_row_malloc_ptr[back_buffer_id].rowbits[color_depth_idx]; //matrixUpdateFrames location to write to uint16_t's + // We need to update the correct uint16_t in the rowBitStruct array, that gets sent out in parallel + uint16_t &v = p->data[rowBitStruct_x_coord_uint16_t_position]; // persist what we already have + if (painting_top_frame) + { // Need to copy what the RGB status is for the bottom pixels + v &= BITSMASK_RGB1; // reset R1G1B1 bits + // 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; } + + } else { // Do it the other way around + v &= BITSMASK_RGB2; // reset R2G2B2 bits + // Color to set + if (red & mask) { v|=BIT_R2; } + if (green & mask) { v|=BIT_G2; } + if (blue & mask) { v|=BIT_B2; } + } // paint + + if (fastmode) + continue; + + // update address/control bits + v &= BITSMASK_CTRL; // reset ABCDE,EO,LAT address bits + uint16_t _y = color_depth_idx ? y_coord : y_coord -1; + v|=_y<<8; // shift row coord to match ABCDE bits from 8 to 12 and set bitvector - // int v = p->data[rowBitStruct_x_coord_uint16_t_position]; // persist what we already have - int v=0; // the output bitstream - - // if there is no latch to hold address, output ADDX lines directly to GPIO and latch data at end of cycle - int gpioRowAddress = y_coord; - - // normally output current rows ADDX, special case for LSB, output previous row's ADDX (as previous row is being displayed for one latch cycle) - if(color_depth_idx == 0) - gpioRowAddress = y_coord-1; - - if (gpioRowAddress & 0x01) v|=BIT_A; // 1 - if (gpioRowAddress & 0x02) v|=BIT_B; // 2 - if (gpioRowAddress & 0x04) v|=BIT_C; // 4 - if (gpioRowAddress & 0x08) v|=BIT_D; // 8 - if (gpioRowAddress & 0x10) v|=BIT_E; // 16 - - // need to disable OE after latch to hide row transition - if((x_coord) == 0 ) v|=BIT_OE; - // drive latch while shifting out last bit of RGB data if((x_coord) == PIXELS_PER_ROW-1) v|=BIT_LAT; - - // need to turn off OE one clock before latch, otherwise can get ghosting - if((x_coord)==PIXELS_PER_ROW-2) v|=BIT_OE; - - // turn off OE after brightness value is reached when displaying MSBs - // MSBs always output normal brightness - // LSB (!color_depth_idx) outputs normal brightness as MSB from previous row is being displayed - if((color_depth_idx > lsbMsbTransitionBit || !color_depth_idx) && ((x_coord) >= brightness)) v|=BIT_OE; // For Brightness - + + // need to disable OE after latch to hide row transition + // OR one clock before latch, otherwise can get ghosting + if((x_coord) == 0 || (x_coord)==PIXELS_PER_ROW-2){ v|=BIT_OE; continue;} + + if((color_depth_idx > lsbMsbTransitionBit || !color_depth_idx) && ((x_coord) >= brightness)) + {v|=BIT_OE; continue;}// For Brightness + // 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; // For Brightness } - /* // Development / testing code only. Serial.printf("r value of %d, color depth: %d, mask: %d\r\n", red, color_depth_idx, mask); @@ -620,64 +548,10 @@ void MatrixPanel_I2S_DMA::updateMatrixDMABuffer(int16_t x_coord, int16_t y_coord Serial.printf("val2pwm r value: %d\r\n", val2PWM(red)); if (val2PWM(red) & mask) { Serial.println("Success - PWM"); v|=BIT_R2; } */ - - - if (painting_top_frame) - { // 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[rowBitStruct_x_coord_uint16_t_position] & BIT_R2) - v|=BIT_R2; - - if (p->data[rowBitStruct_x_coord_uint16_t_position] & BIT_G2) - v|=BIT_G2; - - if (p->data[rowBitStruct_x_coord_uint16_t_position] & 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 / persist - if (p->data[rowBitStruct_x_coord_uint16_t_position] & BIT_R1) - v|=BIT_R1; - - if (p->data[rowBitStruct_x_coord_uint16_t_position] & BIT_G1) - v|=BIT_G1; - - if (p->data[rowBitStruct_x_coord_uint16_t_position] & 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 - */ - - // 16 bit parallel mode - p->data[rowBitStruct_x_coord_uint16_t_position] = v; - - } // color depth loop (8) + } // updateMatrixDMABuffer (specific co-ords change) -#endif /* Update the entire buffer with a single specific colour - quicker */ @@ -692,91 +566,73 @@ void MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint8_t red, uint8_t green, uint blue = val2PWM(blue); */ red = lumConvTab[red]; - green = lumConvTab[green]; + green = lumConvTab[green]; blue = lumConvTab[blue]; - for (unsigned int matrix_frame_parallel_row = 0; matrix_frame_parallel_row < ROWS_PER_FRAME; matrix_frame_parallel_row++) // half height - 16 iterations - { - for(int color_depth_idx=0; color_depth_idx<PIXEL_COLOR_DEPTH_BITS; color_depth_idx++) // color depth - 8 iterations + for(uint8_t color_depth_idx=0; color_depth_idx<PIXEL_COLOR_DEPTH_BITS; color_depth_idx++) // color depth - 8 iterations + { + // let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer + uint16_t RGBbitfield = 0; + uint8_t mask = (1 << color_depth_idx); // 24 bit color + + RGBbitfield |= (bool)(blue & mask); + RGBbitfield <<= 1; + RGBbitfield |= (bool)(green & mask); + RGBbitfield <<= 1; + RGBbitfield |= (bool)(red & mask); + RGBbitfield |= RGBbitfield << 3; // now we should have 6 bits of RGB suitable for DMA buffer + //Serial.printf("Fill with: 0x%#06x\n", RGBbitfield); + + // iterate rows + for (uint16_t matrix_frame_parallel_row = 0; matrix_frame_parallel_row < ROWS_PER_FRAME; matrix_frame_parallel_row++) // half height - 16 iterations { - uint16_t mask = (1 << color_depth_idx); // 24 bit color - - // The destination for the pixel bitstream - //rowBitStruct *p = &matrix_framebuffer_malloc_1[back_buffer_id].rowdata[matrix_frame_parallel_row].rowbits[color_depth_idx]; //matrixUpdateFrames location to write to uint16_t's - rowColorDepthStruct *fb_row_malloc_ptr = (rowColorDepthStruct *) matrix_row_framebuffer_malloc[matrix_frame_parallel_row]; - //Serial.printf("Accessing fb address: %d\r\n", fb_row_malloc_ptr); - - rowBitStruct *p = &fb_row_malloc_ptr[back_buffer_id].rowbits[color_depth_idx]; //matrixUpdateFrames location to write to uint16_t's + rowColorDepthStruct *fb_row_malloc_ptr = (rowColorDepthStruct *) matrix_row_framebuffer_malloc[matrix_frame_parallel_row]; + //Serial.printf("Accessing fb address: %d\r\n", fb_row_malloc_ptr); + + // The destination for the pixel bitstream + rowBitStruct *p = &fb_row_malloc_ptr[back_buffer_id].rowbits[color_depth_idx]; //matrixUpdateFrames location to write to uint16_t's + + // iterate pixels in a row + if (fastmode){ + for(uint16_t x_coord=0; x_coord < MATRIX_WIDTH; x_coord++){ + uint16_t &v = p->data[(x_coord % 2) ? (x_coord-1):(x_coord+1)]; // take reference to bit vector + v &= BITSMASK_RGB12; // reset color bits + v |= RGBbitfield; // set new color bits + } + } else { + // Set ABCDE address bits vector + uint16_t _y = color_depth_idx ? matrix_frame_parallel_row : matrix_frame_parallel_row -1; + _y <<=8; // shift row y-coord to match ABCDE bits in vector from 8 to 12 + + for(uint16_t x_coord=0; x_coord < MATRIX_WIDTH; x_coord++){ + uint16_t &v = p->data[(x_coord % 2) ? (x_coord-1):(x_coord+1)]; // persist what we already have + v = RGBbitfield; // set colot bits and reset all others + v|=_y; // set ABCDE address bits for current row - for(int x_coord=0; x_coord < MATRIX_WIDTH; x_coord++) // row pixel width 64 iterations - { - - int v=0; // the output bitstream - - // if there is no latch to hold address, output ADDX lines directly to GPIO and latch data at end of cycle - int gpioRowAddress = matrix_frame_parallel_row; - - // normally output current rows ADDX, special case for LSB, output previous row's ADDX (as previous row is being displayed for one latch cycle) - if(color_depth_idx == 0) - gpioRowAddress = matrix_frame_parallel_row-1; - - if (gpioRowAddress & 0x01) v|=BIT_A; // 1 - if (gpioRowAddress & 0x02) v|=BIT_B; // 2 - if (gpioRowAddress & 0x04) v|=BIT_C; // 4 - if (gpioRowAddress & 0x08) v|=BIT_D; // 8 - if (gpioRowAddress & 0x10) v|=BIT_E; // 16 - - - /* ORIG - // need to disable OE after latch to hide row transition - if((x_coord) == 0) v|=BIT_OE; - - // drive latch while shifting out last bit of RGB data - if((x_coord) == PIXELS_PER_LATCH-1) v|=BIT_LAT; - - // need to turn off OE one clock before latch, otherwise can get ghosting - if((x_coord)==PIXELS_PER_LATCH-1) v|=BIT_OE; - */ - - // need to disable OE after latch to hide row transition - if((x_coord) == 0 ) v|=BIT_OE; - // drive latch while shifting out last bit of RGB data if((x_coord) == PIXELS_PER_ROW-1) v|=BIT_LAT; - - // need to turn off OE one clock before latch, otherwise can get ghosting - if((x_coord)==PIXELS_PER_ROW-2) v|=BIT_OE; - - - // turn off OE after brightness value is reached when displaying MSBs - // MSBs always output normal brightness - // LSB (!color_depth_idx) outputs normal brightness as MSB from previous row is being displayed - if((color_depth_idx > lsbMsbTransitionBit || !color_depth_idx) && ((x_coord) >= brightness)) v|=BIT_OE; // For Brightness - + + // need to disable OE after latch to hide row transition + // OR one clock before latch, otherwise can get ghosting + if(!x_coord || (x_coord)==PIXELS_PER_ROW-2){ + v|=BIT_OE; continue; + } + + // BRT OE + if((color_depth_idx > lsbMsbTransitionBit || !color_depth_idx) && ((x_coord) >= brightness)){ + v|=BIT_OE; continue; // For Brightness control + } + // 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; // For Brightness } - - // Top and bottom matrix MATRIX_ROWS_IN_PARALLEL half colours - if (green & mask) { v|=BIT_G1; v|=BIT_G2; } - if (blue & mask) { v|=BIT_B1; v|=BIT_B2; } - if (red & mask) { v|=BIT_R1; v|=BIT_R2; } - - // 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 - - } // end x_coord iteration - } // colour depth loop (8) - } // end row iteration - + } // end of x-iterator + } // end x_coord iteration + } // end row iteration + } // colour depth loop (8) } // updateMatrixDMABuffer (full frame paint) /** @@ -791,93 +647,65 @@ void MatrixPanel_I2S_DMA::shiftDriver(const shift_driver _drv, const int dma_r1_ #if SERIAL_DEBUG Serial.println( F("MatrixPanel_I2S_DMA - initializing FM6124 driver...")); #endif - int C12[16] = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - int C13[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}; + bool REG1[16] = {0,0,0,0,0, 1,1,1,1,1,1, 0,0,0,0,0}; // this sets global matrix brightness power + bool REG2[16] = {0,0,0,0,0, 0,0,0,0,1,0, 0,0,0,0,0}; // a single bit enables the matrix output - pinMode(dma_r1_pin, OUTPUT); - pinMode(dma_g1_pin, OUTPUT); - pinMode(dma_b1_pin, OUTPUT); - pinMode(dma_r2_pin, OUTPUT); - pinMode(dma_g2_pin, OUTPUT); - pinMode(dma_b2_pin, OUTPUT); - pinMode(dma_a_pin, OUTPUT); - pinMode(dma_b_pin, OUTPUT); - pinMode(dma_c_pin, OUTPUT); - pinMode(dma_d_pin, OUTPUT); - pinMode(dma_e_pin, OUTPUT); - pinMode(dma_clk_pin, OUTPUT); - pinMode(dma_lat_pin, OUTPUT); - pinMode(dma_oe_pin, OUTPUT); + for (uint8_t _pin:{dma_r1_pin, dma_r2_pin, dma_g1_pin, dma_g2_pin, dma_b1_pin, dma_b2_pin, dma_clk_pin, dma_lat_pin, dma_oe_pin}) + pinMode(_pin, OUTPUT); - // Send Data to control register 11 - digitalWrite(dma_oe_pin, HIGH); // Display reset + digitalWrite(dma_oe_pin, HIGH); // Disable Display digitalWrite(dma_lat_pin, LOW); digitalWrite(dma_clk_pin, LOW); - for (int l = 0; l < MATRIX_WIDTH; l++){ - int y = l % 16; - digitalWrite(dma_r1_pin, LOW); - digitalWrite(dma_g1_pin, LOW); - digitalWrite(dma_b1_pin, LOW); - digitalWrite(dma_r2_pin, LOW); - digitalWrite(dma_g2_pin, LOW); - digitalWrite(dma_b2_pin, LOW); - if (C12[y] == 1){ - digitalWrite(dma_r1_pin, HIGH); - digitalWrite(dma_g1_pin, HIGH); - digitalWrite(dma_b1_pin, HIGH); - digitalWrite(dma_r2_pin, HIGH); - digitalWrite(dma_g2_pin, HIGH); - digitalWrite(dma_b2_pin, HIGH); - } + // Send Data to control register REG1 + // this sets the matrix brightness actually + for (int l = 0; l < MATRIX_WIDTH; l++){ + for (uint8_t _pin:{dma_r1_pin, dma_r2_pin, dma_g1_pin, dma_g2_pin, dma_b1_pin, dma_b2_pin}) + digitalWrite(_pin, REG1[l%16]); // we have 16 bits shifters and write the same value all over the matrix array - if (l > MATRIX_WIDTH - 12){ + if (l > MATRIX_WIDTH - 12){ // pull the latch 11 clocks before the end of matrix so that REG1 starts counting to save the value digitalWrite(dma_lat_pin, HIGH); - } else { - digitalWrite(dma_lat_pin, LOW); } - - digitalWrite(dma_clk_pin, HIGH); + digitalWrite(dma_clk_pin, HIGH); // 1-clock pulse digitalWrite(dma_clk_pin, LOW); } + // drop the latch and save data to the REG1 all over the FM6124 chips digitalWrite(dma_lat_pin, LOW); digitalWrite(dma_clk_pin, LOW); - // Send Data to control register 12 + // Send Data to control register REG2 (enable LED output) for (int l = 0; l < MATRIX_WIDTH; l++){ - int y = l % 16; - digitalWrite(dma_r1_pin, LOW); - digitalWrite(dma_g1_pin, LOW); - digitalWrite(dma_b1_pin, LOW); - digitalWrite(dma_r2_pin, LOW); - digitalWrite(dma_g2_pin, LOW); - digitalWrite(dma_b2_pin, LOW); - - if (C13[y] == 1){ - digitalWrite(dma_r1_pin, HIGH); - digitalWrite(dma_g1_pin, HIGH); - digitalWrite(dma_b1_pin, HIGH); - digitalWrite(dma_r2_pin, HIGH); - digitalWrite(dma_g2_pin, HIGH); - digitalWrite(dma_b2_pin, HIGH); - } + for (uint8_t _pin:{dma_r1_pin, dma_r2_pin, dma_g1_pin, dma_g2_pin, dma_b1_pin, dma_b2_pin}) + digitalWrite(_pin, REG2[l%16]); // we have 16 bits shifters and we write the same value all over the matrix array - if (l > MATRIX_WIDTH - 13){ + if (l > MATRIX_WIDTH - 13){ // pull the latch 12 clocks before the end of matrix so that reg2 stars counting to save the value digitalWrite(dma_lat_pin, HIGH); - } else { - digitalWrite(dma_lat_pin, LOW); } - digitalWrite(dma_clk_pin, HIGH); + digitalWrite(dma_clk_pin, HIGH); // 1-clock pulse digitalWrite(dma_clk_pin, LOW); } + // drop the latch and save data to the REG1 all over the FM6126 chips digitalWrite(dma_lat_pin, LOW); digitalWrite(dma_clk_pin, LOW); - break; } + break; case SHIFT: default: break; } } + +/** + * clear screen to black and reset service bits + */ +void MatrixPanel_I2S_DMA::clearScreen(){ + if (fastmode) { + fastmode = false; // we always clear screen in 'slow' mode to update all bits in DMA buffer + updateMatrixDMABuffer(0, 0, 0); + fastmode = true; // restore fastmode + } else { + updateMatrixDMABuffer(0, 0, 0); + } +}
\ No newline at end of file diff --git a/ESP32-HUB75-MatrixPanel-I2S-DMA.h b/ESP32-HUB75-MatrixPanel-I2S-DMA.h index 913a245..e20ee33 100644 --- a/ESP32-HUB75-MatrixPanel-I2S-DMA.h +++ b/ESP32-HUB75-MatrixPanel-I2S-DMA.h @@ -104,6 +104,11 @@ #define BIT_D (1<<11) #define BIT_E (1<<12) +#define BITSMASK_RGB1 (0xfff8) // inverted bitmask for R1G1B1 bit in pixel vector +#define BITSMASK_RGB2 (0xffc7) // inverted bitmask for R2G2B2 bit in pixel vector +#define BITSMASK_RGB12 (0xffc0) // inverted bitmask for R1G1B1R2G2B2 bit in pixel vector +#define BITSMASK_CTRL (0xe03f) // inverted bitmask for control bits ABCDE,LAT,OE in pixel vector + // RGB Panel Constants / Calculated Values #define COLOR_CHANNELS_PER_PIXEL 3 #define PIXELS_PER_ROW ((MATRIX_WIDTH * MATRIX_HEIGHT) / MATRIX_HEIGHT) // = 64 @@ -114,8 +119,10 @@ /* Keep this as is. Do not change. */ #define ESP32_I2S_DMA_MODE I2S_PARALLEL_BITS_16 // Pump 16 bits out in parallel #define ESP32_I2S_DMA_STORAGE_TYPE uint16_t // one uint16_t at a time. -//#define ESP32_I2S_CLOCK_SPEED (20000000UL) // @ 20Mhz -#define ESP32_I2S_CLOCK_SPEED (10000000UL) // @ 10Mhz +#ifndef ESP32_I2S_CLOCK_SPEED + #define ESP32_I2S_CLOCK_SPEED (10000000UL) // @ 10Mhz +//#define ESP32_I2S_CLOCK_SPEED (20000000UL) // @ 20Mhz +#endif #define CLKS_DURING_LATCH 0 // Not used. /***************************************************************************************/ @@ -250,8 +257,8 @@ class MatrixPanel_I2S_DMA : public Adafruit_GFX { // Draw pixels virtual void drawPixel(int16_t x, int16_t y, uint16_t color); // overwrite adafruit implementation virtual void fillScreen(uint16_t color); // overwrite adafruit implementation - void clearScreen() { fillScreen(0); } - void fillScreenRGB888(uint8_t r, uint8_t g, uint8_t b); + void clearScreen(); + void fillScreenRGB888(uint8_t r, uint8_t g, uint8_t b); void drawPixelRGB565(int16_t x, int16_t y, uint16_t color); void drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b); void drawPixelRGB24(int16_t x, int16_t y, RGB24 color); @@ -296,18 +303,46 @@ class MatrixPanel_I2S_DMA : public Adafruit_GFX { while(!i2s_parallel_is_previous_buffer_free()) {} } - inline void setPanelBrightness(int b) { // Change to set the brightness of the display, range of 1 to matrixWidth (i.e. 1 - 64) brightness = b; + if (fastmode) // in 'fast' mode we should always reset DMA buffer to update OE bits that controls brightness + clearScreen(); // and YES, it WILL flicker. you've been warned :) } - + + /** + * this is just a wrapper to control brightness + * with an 8-bit value (0-255), very popular in FastLED-based sketches :) + * @param uint8_t b - 8-bit brightness value + */ + void setBrightness8(const uint8_t b) + { + setPanelBrightness(b * MATRIX_WIDTH / 256); + } + inline void setMinRefreshRate(int rr) { min_refresh_rate = rr; } + /** + * Controls fast-update mode, when only RGB bits are upated in DMA buffer + * @param mode bool - set/clear fastmode. Will take effect on newly updated pixels only + * @return fastmode status + */ + bool setFastMode(const bool mode){ + fastmode = mode; + return fastmode; + }; + + /** + * Controls fast-update mode, when only RGB bits are upated in DMA buffer + * @param void - returns current fastmode status + */ + bool setFastMode(){return fastmode;}; + + int calculated_refresh_rate = 0; // ------- PRIVATE ------- @@ -332,6 +367,18 @@ class MatrixPanel_I2S_DMA : public Adafruit_GFX { int min_refresh_rate = 99; // Probably best to leave as is unless you want to experiment. Framerate has an impact on brightness and also power draw - voltage ripple. int lsbMsbTransitionBit = 0; // For possible color depth calculations + /** + * this var controls how DMA buffers are updated + * if set to false (default) - full recalculation performed, including address line bits, OE, LAT + * if set to true, only RGB1, RGB2 bits are updated. + * Could be toggled any time, see Notes regarding #define GO_FOR_SPEED 1 above + */ + #ifdef GO_FOR_SPEED + bool fastmode = true; + #else + bool fastmode = false; + #endif + /* Calculate the memory available for DMA use, do some other stuff, and allocate accordingly */ bool allocateDMAmemory(); diff --git a/FM6126A.md b/FM6126A.md new file mode 100644 index 0000000..ac4d395 --- /dev/null +++ b/FM6126A.md @@ -0,0 +1,68 @@ +## The mystery of control registers for FM6126A chips + + + +Datasheet for this chis chip is in chineese and does not shed a light on what those two control regs are. + +An excellent insight could be found here + +https://github.com/hzeller/rpi-rgb-led-matrix/issues/746#issuecomment-453860510 + + + + +So there are two regs - **REG1** and **REG1**, + +one could be written with 12 clock pusles (and usually called reg12, dunno why :)) + +the other one could be written with 13 clock pusles (and usually called reg13, dunno why :)) + + + +So I've done some measurmens on power consumption while toggling bits of **REG1** and it looks that it could provide a fine grained brighness control over matrix. + +There are 6 bits (6 to 11) giving an increased brighness (compared to all-zeroes) and 4 bits (2-5) giving decreased brighness!!! + + + +So it seems that the most bright (and hungry for power) value is + +bool REG1[16] = {0,0,0,0,0, 1,1,1,1,1,1, 0,0,0,0,0}; and not {0,1,1,1,1, 1,1,1,1,1,1, 1,1,1,1,1} as it is usually used. + +I'm not sure about bit 1 - it is either not used or I was unable to measure it's influence to brightness/power. + + + +Giving at least 10 bits of hardware brightness control opens pretty nice options for offloading. Should dig into this more deeper. + + + +Here are some of the measurments I've took for 2 64x64 panels filled with white color - reg value and corresponding current drain in amps. + + + +|REG1 |bit value|Current, amps | +|--|--|--| +|REG1| 0111111 00000| >5 amps| +|REG1| 0100010 00000| 3.890 amp| +|REG1| 0100000 00000| 3.885 amp| +|REG1| 0011110 00000| 3.640 amp| +|REG1| 0011100 00000| 3.620 amp| +|REG1| 0011000 00000| 3.240 amp| +|REG1| 0010010 00000| 2.520 amp| +|REG1| 0010001 00000| 2.518 amp| +|REG1| 0010001 10000| 2.493 amp| +|REG1| 0010000 00000| 2.490 amp| +|REG1| 0010000 11110| 2.214 amp| +|REG1| 0001100 00000| 2.120 amp| +|REG1| 0001000 00000| 1.750 amp| +|REG1| 0000100 00000| 1.375 amp| +|REG1| 0000010 00000| 1.000 amp| +|REG1| **0000000 00000**| 0.995 amp| +|REG1| 0000001 11111| 0.700 amp| +|REG1| 0000000 01111| 0.690 amp| +|REG1| 0000000 10000| 0.690 amp| +|REG1| 0000000 11110| 0.686 amp| + + +/Vortigont/
\ No newline at end of file diff --git a/examples/AuroraDemo/AuroraDemo.ino b/examples/AuroraDemo/AuroraDemo.ino index a85d524..002a1ed 100644 --- a/examples/AuroraDemo/AuroraDemo.ino +++ b/examples/AuroraDemo/AuroraDemo.ino @@ -47,6 +47,19 @@ void setup() Serial.begin(115200); delay(250); matrix.begin(R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN ); // setup the LED matrix + /** + * this demos runs pretty fine in fast-mode which gives much better fps on large matrixes (>128x64) + * see comments in the lib header on what does that means + */ + //dma_display.setFastMode(true); + + // SETS THE BRIGHTNESS HERE. MAX value is MATRIX_WIDTH, 2/3 OR LOWER IDEAL, default is about 50% + // dma_display.setPanelBrightness(30); + /* another way to change brightness is to use + * dma_display.setPanelBrightness8(uint8_t brt); // were brt is within range 0-255 + * it will recalculate to consider matrix width automatically + */ + //dma_display.setPanelBrightness8(180); Serial.println("**************** Starting Aurora Effects Demo ****************"); diff --git a/examples/AuroraDemo/PatternSwirl.h b/examples/AuroraDemo/PatternSwirl.h index e3baf2f..74e5305 100644 --- a/examples/AuroraDemo/PatternSwirl.h +++ b/examples/AuroraDemo/PatternSwirl.h @@ -44,7 +44,7 @@ class PatternSwirl : public Drawable { // Note that we never actually clear the matrix, we just constantly // blur it repeatedly. Since the blurring is 'lossy', there's // an automatic trend toward black -- by design. - uint8_t blurAmount = beatsin8(2, 10, 255) + uint8_t blurAmount = beatsin8(2, 10, 255); #if FASTLED_VERSION >= 3001000 blur2d(effects.leds, MATRIX_WIDTH > 255 ? 255 : MATRIX_WIDTH, MATRIX_HEIGHT > 255 ? 255 : MATRIX_HEIGHT, blurAmount); diff --git a/examples/FM6126Panel/FM6126Panel.ino b/examples/FM6126Panel/FM6126Panel.ino index e5d92bc..45eae70 100644 --- a/examples/FM6126Panel/FM6126Panel.ino +++ b/examples/FM6126Panel/FM6126Panel.ino @@ -44,8 +44,8 @@ MatrixPanel_I2S_DMA dma_display; // End of default setup for RGB Matrix 64x32 panel /////////////////////////////////////////////////////////////// -int time_counter = 0; -int cycles = 0; +uint16_t time_counter = 0, cycles = 0, fps = 0; +unsigned long fps_timer; CRGB currentColor; CRGBPalette16 palettes[] = {HeatColors_p, LavaColors_p, RainbowColors_p, RainbowStripeColors_p, CloudColors_p}; @@ -62,13 +62,28 @@ void setup(){ // Panels are the same - some seem to display ghosting artefacts at lower brightness levels. // In the setup() function do something like: - dma_display.setPanelBrightness(30); // SETS THE BRIGHTNESS HERE. 60 OR LOWER IDEAL. + // SETS THE BRIGHTNESS HERE. MAX value is MATRIX_WIDTH, 2/3 OR LOWER IDEAL, default is about 50% + // dma_display.setPanelBrightness(30); + + /* another way to change brightness is to use + * dma_display.setPanelBrightness8(uint8_t brt); // were brt is within range 0-255 + * it will recalculate to consider matrix width automatically + */ + //dma_display.setPanelBrightness8(180); + + /** + * this demo runs pretty fine in fast-mode which gives much better fps on large matrixes (>128x64) + * see comments in the lib header on what does that means + */ + dma_display.setFastMode(true); /** - * be sure to specify 'FM6126A' as last parametr to the begin(), + * Run display on our matrix, be sure to specify 'FM6126A' as last parametr to the begin(), * it would reset 6126 registers and enables the matrix */ dma_display.begin(R1, G1, BL1, R2, G2, BL2, CH_A, CH_B, CH_C, CH_D, CH_E, LAT, OE, CLK, FM6126A); + + fps_timer = millis(); } void loop(){ @@ -85,12 +100,21 @@ void loop(){ } } - time_counter += 1; - cycles++; + ++time_counter; + ++cycles; + ++fps; if (cycles >= 1024) { time_counter = 0; cycles = 0; currentPalette = palettes[random(0,sizeof(palettes)/sizeof(palettes[0]))]; } + + // print FPS rate every 5 seconds + // Note: this is NOT a matrix refresh rate, it's the number of data frames being drawn to the DMA buffer per second + if (fps_timer + 5000 < millis()){ + Serial.printf_P(PSTR("Effect fps: %d\n"), fps/5); + fps_timer = millis(); + fps = 0; + } }
\ No newline at end of file diff --git a/fillrate.md b/fillrate.md new file mode 100644 index 0000000..b07e1af --- /dev/null +++ b/fillrate.md @@ -0,0 +1,25 @@ +## Estimating fillrate + +Here are some results of simple tests on filling DMA buffer with data. +Filling DMA buffer requres lot's of memory operations on a bit level rather than doing simple byte/word wide store and copy. And it looks like it's quite a task both for esp32 core and compiler. +I've done this while optimizing loops and bit logic along with testing compiler results. + +So the testbed is: + - Matrix modules: 4 x FM6126A based 64x64 modules chained in 256x64 + +A simple sketch: + - allocating single DMA buffs for 256x64 + - allocating (NUM_LEDS*3) bytes for CRGB buffer + - measuring microseconds for the following calls: + - clearScreen() - full blanking + - fillScreenRGB888() with monochrome/gray colors + - filling some gradient into CRGB buff + - painting CRGB buff into DMA buff with looped drawPixelRGB888() + +|Pattern |Reference|Ref+SPEED|Optimized|Optimized+SPEED| +|--|--|--|--|--| +|fillScreenRGB888()|14570|14570|13400 (8.5% faster)|5520 (164% faster)| +|CRGB buff fill|760|760|760|760| +|updateMatrixDMABuffer(CRGB)|7700|32080|55780 (38% faster)|33500(+4.2% slower)| + +to be continued...
\ No newline at end of file |
