Merge pull request #44 from vortigont/brtctrl
FastMode with brightness control + various other code optimisations.
This commit is contained in:
commit
3cda03ab83
7 changed files with 340 additions and 335 deletions
|
@ -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);
|
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 the ESP crashes here, then we must have a horribly fragmented memory space, or trying to allocate a ludicrous resolution.
|
||||||
#if SERIAL_DEBUG
|
#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
|
#endif
|
||||||
if ( matrix_row_framebuffer_malloc[malloc_num] == NULL ) {
|
if ( matrix_row_framebuffer_malloc[malloc_num] == NULL ) {
|
||||||
Serial.printf("ERROR: Couldn't malloc matrix_row_framebuffer %d! Critical fail.\r\n", malloc_num);
|
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.
|
* 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 */
|
/* 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. */
|
/* 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)
|
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 ( !everything_OK ) {
|
||||||
|
|
||||||
#if SERIAL_DEBUG
|
#if SERIAL_DEBUG
|
||||||
Serial.println("Cannot updateMatrixDMABuffer as setup failed!");
|
Serial.println("Cannot updateMatrixDMABuffer as setup failed!");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return;
|
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.
|
||||||
/* 1) Check that the co-ordinates are within range, or it'll break everything big time.
|
* Valid co-ordinates are from 0 to (MATRIX_XXXX-1)
|
||||||
* Valid co-ordinates are from 0 to (MATRIX_XXXX-1)
|
*/
|
||||||
*/
|
if ( x_coord < 0 || y_coord < 0 || x_coord >= MATRIX_WIDTH || y_coord >= MATRIX_HEIGHT) {
|
||||||
if ( x_coord < 0 || y_coord < 0 || x_coord >= MATRIX_WIDTH || y_coord >= MATRIX_HEIGHT) {
|
return;
|
||||||
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,
|
/* 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
|
* 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.
|
* data.
|
||||||
*/
|
*/
|
||||||
bool painting_top_frame = true;
|
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)
|
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;
|
painting_top_frame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to update the correct uint16_t in the rowBitStruct array, that gets sent out in parallel
|
// Find the memory address for the malloc for this framebuffer row.
|
||||||
int rowBitStruct_x_coord_uint16_t_position = (x_coord % 2) ? (x_coord-1):(x_coord+1);
|
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
|
|
||||||
{
|
|
||||||
int 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.
|
// We need to update the correct uint16_t in the rowBitStruct array, that gets sent out in parallel
|
||||||
rowColorDepthStruct *fb_row_malloc_ptr = (rowColorDepthStruct *) matrix_row_framebuffer_malloc[y_coord];
|
// 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
|
||||||
|
{
|
||||||
|
uint8_t mask = (1 << color_depth_idx); // 24 bit color
|
||||||
|
|
||||||
// Get the contents at this address, cast as a rowColorDepthStruct
|
// 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
|
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
|
// drive latch while shifting out last bit of RGB data
|
||||||
if((x_coord) == PIXELS_PER_ROW-1) v|=BIT_LAT;
|
if((x_coord) == PIXELS_PER_ROW-1) v|=BIT_LAT;
|
||||||
|
|
||||||
// need to turn off OE one clock before latch, otherwise can get ghosting
|
// need to disable OE after latch to hide row transition
|
||||||
if((x_coord)==PIXELS_PER_ROW-2) v|=BIT_OE;
|
// OR one clock before latch, otherwise can get ghosting
|
||||||
|
if((x_coord) == 0 || (x_coord)==PIXELS_PER_ROW-2){ v|=BIT_OE; continue;}
|
||||||
// turn off OE after brightness value is reached when displaying MSBs
|
|
||||||
// MSBs always output normal brightness
|
if((color_depth_idx > lsbMsbTransitionBit || !color_depth_idx) && ((x_coord) >= brightness))
|
||||||
// LSB (!color_depth_idx) outputs normal brightness as MSB from previous row is being displayed
|
{v|=BIT_OE; continue;}// For Brightness
|
||||||
if((color_depth_idx > lsbMsbTransitionBit || !color_depth_idx) && ((x_coord) >= brightness)) v|=BIT_OE; // 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
|
// 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) {
|
if(color_depth_idx && color_depth_idx <= lsbMsbTransitionBit) {
|
||||||
// divide brightness in half for each bit below lsbMsbTransitionBit
|
// divide brightness in half for each bit below lsbMsbTransitionBit
|
||||||
int lsbBrightness = brightness >> (lsbMsbTransitionBit - color_depth_idx + 1);
|
int lsbBrightness = brightness >> (lsbMsbTransitionBit - color_depth_idx + 1);
|
||||||
if((x_coord) >= lsbBrightness) v|=BIT_OE; // For Brightness
|
if((x_coord) >= lsbBrightness) v|=BIT_OE; // For Brightness
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Development / testing code only.
|
// Development / testing code only.
|
||||||
Serial.printf("r value of %d, color depth: %d, mask: %d\r\n", red, color_depth_idx, mask);
|
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));
|
Serial.printf("val2pwm r value: %d\r\n", val2PWM(red));
|
||||||
if (val2PWM(red) & mask) { Serial.println("Success - PWM"); v|=BIT_R2; }
|
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)
|
} // color depth loop (8)
|
||||||
|
|
||||||
|
|
||||||
} // updateMatrixDMABuffer (specific co-ords change)
|
} // updateMatrixDMABuffer (specific co-ords change)
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Update the entire buffer with a single specific colour - quicker */
|
/* 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);
|
blue = val2PWM(blue);
|
||||||
*/
|
*/
|
||||||
red = lumConvTab[red];
|
red = lumConvTab[red];
|
||||||
green = lumConvTab[green];
|
green = lumConvTab[green];
|
||||||
blue = lumConvTab[blue];
|
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(uint8_t color_depth_idx=0; color_depth_idx<PIXEL_COLOR_DEPTH_BITS; color_depth_idx++) // color depth - 8 iterations
|
||||||
{
|
{
|
||||||
for(int 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;
|
||||||
uint16_t mask = (1 << color_depth_idx); // 24 bit color
|
uint8_t mask = (1 << color_depth_idx); // 24 bit color
|
||||||
|
|
||||||
// The destination for the pixel bitstream
|
RGBbitfield |= (bool)(blue & mask);
|
||||||
//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
|
RGBbitfield <<= 1;
|
||||||
rowColorDepthStruct *fb_row_malloc_ptr = (rowColorDepthStruct *) matrix_row_framebuffer_malloc[matrix_frame_parallel_row];
|
RGBbitfield |= (bool)(green & mask);
|
||||||
//Serial.printf("Accessing fb address: %d\r\n", fb_row_malloc_ptr);
|
RGBbitfield <<= 1;
|
||||||
|
RGBbitfield |= (bool)(red & mask);
|
||||||
rowBitStruct *p = &fb_row_malloc_ptr[back_buffer_id].rowbits[color_depth_idx]; //matrixUpdateFrames location to write to uint16_t's
|
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
|
||||||
|
{
|
||||||
|
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
|
// drive latch while shifting out last bit of RGB data
|
||||||
if((x_coord) == PIXELS_PER_ROW-1) v|=BIT_LAT;
|
if((x_coord) == PIXELS_PER_ROW-1) v|=BIT_LAT;
|
||||||
|
|
||||||
// need to turn off OE one clock before latch, otherwise can get ghosting
|
// need to disable OE after latch to hide row transition
|
||||||
if((x_coord)==PIXELS_PER_ROW-2) v|=BIT_OE;
|
// OR one clock before latch, otherwise can get ghosting
|
||||||
|
if(!x_coord || (x_coord)==PIXELS_PER_ROW-2){
|
||||||
|
v|=BIT_OE; continue;
|
||||||
// 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
|
// BRT OE
|
||||||
if((color_depth_idx > lsbMsbTransitionBit || !color_depth_idx) && ((x_coord) >= brightness)) v|=BIT_OE; // For Brightness
|
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
|
// 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) {
|
if(color_depth_idx && color_depth_idx <= lsbMsbTransitionBit) {
|
||||||
// divide brightness in half for each bit below lsbMsbTransitionBit
|
// divide brightness in half for each bit below lsbMsbTransitionBit
|
||||||
int lsbBrightness = brightness >> (lsbMsbTransitionBit - color_depth_idx + 1);
|
int lsbBrightness = brightness >> (lsbMsbTransitionBit - color_depth_idx + 1);
|
||||||
if((x_coord) >= lsbBrightness) v|=BIT_OE; // For Brightness
|
if((x_coord) >= lsbBrightness) v|=BIT_OE; // For Brightness
|
||||||
}
|
}
|
||||||
|
} // end of x-iterator
|
||||||
// Top and bottom matrix MATRIX_ROWS_IN_PARALLEL half colours
|
} // end x_coord iteration
|
||||||
if (green & mask) { v|=BIT_G1; v|=BIT_G2; }
|
} // end row iteration
|
||||||
if (blue & mask) { v|=BIT_B1; v|=BIT_B2; }
|
} // colour depth loop (8)
|
||||||
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
|
|
||||||
|
|
||||||
} // updateMatrixDMABuffer (full frame paint)
|
} // updateMatrixDMABuffer (full frame paint)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -791,93 +647,65 @@ void MatrixPanel_I2S_DMA::shiftDriver(const shift_driver _drv, const int dma_r1_
|
||||||
#if SERIAL_DEBUG
|
#if SERIAL_DEBUG
|
||||||
Serial.println( F("MatrixPanel_I2S_DMA - initializing FM6124 driver..."));
|
Serial.println( F("MatrixPanel_I2S_DMA - initializing FM6124 driver..."));
|
||||||
#endif
|
#endif
|
||||||
int C12[16] = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
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
|
||||||
int C13[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0};
|
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);
|
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(dma_g1_pin, OUTPUT);
|
pinMode(_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);
|
|
||||||
|
|
||||||
// Send Data to control register 11
|
digitalWrite(dma_oe_pin, HIGH); // Disable Display
|
||||||
digitalWrite(dma_oe_pin, HIGH); // Display reset
|
|
||||||
digitalWrite(dma_lat_pin, LOW);
|
digitalWrite(dma_lat_pin, LOW);
|
||||||
digitalWrite(dma_clk_pin, LOW);
|
digitalWrite(dma_clk_pin, LOW);
|
||||||
|
|
||||||
|
// Send Data to control register REG1
|
||||||
|
// this sets the matrix brightness actually
|
||||||
for (int l = 0; l < MATRIX_WIDTH; l++){
|
for (int l = 0; l < MATRIX_WIDTH; l++){
|
||||||
int y = l % 16;
|
for (uint8_t _pin:{dma_r1_pin, dma_r2_pin, dma_g1_pin, dma_g2_pin, dma_b1_pin, dma_b2_pin})
|
||||||
digitalWrite(dma_r1_pin, LOW);
|
digitalWrite(_pin, REG1[l%16]); // we have 16 bits shifters and write the same value all over the matrix array
|
||||||
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){
|
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_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (l > MATRIX_WIDTH - 12){
|
|
||||||
digitalWrite(dma_lat_pin, HIGH);
|
digitalWrite(dma_lat_pin, HIGH);
|
||||||
} else {
|
|
||||||
digitalWrite(dma_lat_pin, LOW);
|
|
||||||
}
|
}
|
||||||
|
digitalWrite(dma_clk_pin, HIGH); // 1-clock pulse
|
||||||
digitalWrite(dma_clk_pin, HIGH);
|
|
||||||
digitalWrite(dma_clk_pin, LOW);
|
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_lat_pin, LOW);
|
||||||
digitalWrite(dma_clk_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++){
|
for (int l = 0; l < MATRIX_WIDTH; l++){
|
||||||
int y = l % 16;
|
for (uint8_t _pin:{dma_r1_pin, dma_r2_pin, dma_g1_pin, dma_g2_pin, dma_b1_pin, dma_b2_pin})
|
||||||
digitalWrite(dma_r1_pin, LOW);
|
digitalWrite(_pin, REG2[l%16]); // we have 16 bits shifters and we write the same value all over the matrix array
|
||||||
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){
|
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_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (l > MATRIX_WIDTH - 13){
|
|
||||||
digitalWrite(dma_lat_pin, HIGH);
|
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);
|
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_lat_pin, LOW);
|
||||||
digitalWrite(dma_clk_pin, LOW);
|
digitalWrite(dma_clk_pin, LOW);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
case SHIFT:
|
case SHIFT:
|
||||||
default:
|
default:
|
||||||
break;
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -104,6 +104,11 @@
|
||||||
#define BIT_D (1<<11)
|
#define BIT_D (1<<11)
|
||||||
#define BIT_E (1<<12)
|
#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
|
// RGB Panel Constants / Calculated Values
|
||||||
#define COLOR_CHANNELS_PER_PIXEL 3
|
#define COLOR_CHANNELS_PER_PIXEL 3
|
||||||
#define PIXELS_PER_ROW ((MATRIX_WIDTH * MATRIX_HEIGHT) / MATRIX_HEIGHT) // = 64
|
#define PIXELS_PER_ROW ((MATRIX_WIDTH * MATRIX_HEIGHT) / MATRIX_HEIGHT) // = 64
|
||||||
|
@ -114,8 +119,10 @@
|
||||||
/* Keep this as is. Do not change. */
|
/* 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_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_DMA_STORAGE_TYPE uint16_t // one uint16_t at a time.
|
||||||
//#define ESP32_I2S_CLOCK_SPEED (20000000UL) // @ 20Mhz
|
#ifndef ESP32_I2S_CLOCK_SPEED
|
||||||
#define ESP32_I2S_CLOCK_SPEED (10000000UL) // @ 10Mhz
|
#define ESP32_I2S_CLOCK_SPEED (10000000UL) // @ 10Mhz
|
||||||
|
//#define ESP32_I2S_CLOCK_SPEED (20000000UL) // @ 20Mhz
|
||||||
|
#endif
|
||||||
#define CLKS_DURING_LATCH 0 // Not used.
|
#define CLKS_DURING_LATCH 0 // Not used.
|
||||||
/***************************************************************************************/
|
/***************************************************************************************/
|
||||||
|
|
||||||
|
@ -250,8 +257,8 @@ class MatrixPanel_I2S_DMA : public Adafruit_GFX {
|
||||||
// Draw pixels
|
// Draw pixels
|
||||||
virtual void drawPixel(int16_t x, int16_t y, uint16_t color); // overwrite adafruit implementation
|
virtual void drawPixel(int16_t x, int16_t y, uint16_t color); // overwrite adafruit implementation
|
||||||
virtual void fillScreen(uint16_t color); // overwrite adafruit implementation
|
virtual void fillScreen(uint16_t color); // overwrite adafruit implementation
|
||||||
void clearScreen() { fillScreen(0); }
|
void clearScreen();
|
||||||
void fillScreenRGB888(uint8_t r, uint8_t g, uint8_t b);
|
void fillScreenRGB888(uint8_t r, uint8_t g, uint8_t b);
|
||||||
void drawPixelRGB565(int16_t x, int16_t y, uint16_t color);
|
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 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);
|
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()) {}
|
while(!i2s_parallel_is_previous_buffer_free()) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void setPanelBrightness(int b)
|
inline void setPanelBrightness(int b)
|
||||||
{
|
{
|
||||||
// Change to set the brightness of the display, range of 1 to matrixWidth (i.e. 1 - 64)
|
// Change to set the brightness of the display, range of 1 to matrixWidth (i.e. 1 - 64)
|
||||||
brightness = b;
|
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)
|
inline void setMinRefreshRate(int rr)
|
||||||
{
|
{
|
||||||
min_refresh_rate = 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;
|
int calculated_refresh_rate = 0;
|
||||||
|
|
||||||
// ------- PRIVATE -------
|
// ------- 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 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
|
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 */
|
/* Calculate the memory available for DMA use, do some other stuff, and allocate accordingly */
|
||||||
bool allocateDMAmemory();
|
bool allocateDMAmemory();
|
||||||
|
|
||||||
|
|
68
FM6126A.md
Normal file
68
FM6126A.md
Normal file
|
@ -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/
|
|
@ -47,6 +47,19 @@ void setup()
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
delay(250);
|
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
|
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 ****************");
|
Serial.println("**************** Starting Aurora Effects Demo ****************");
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ class PatternSwirl : public Drawable {
|
||||||
// Note that we never actually clear the matrix, we just constantly
|
// Note that we never actually clear the matrix, we just constantly
|
||||||
// blur it repeatedly. Since the blurring is 'lossy', there's
|
// blur it repeatedly. Since the blurring is 'lossy', there's
|
||||||
// an automatic trend toward black -- by design.
|
// 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
|
#if FASTLED_VERSION >= 3001000
|
||||||
blur2d(effects.leds, MATRIX_WIDTH > 255 ? 255 : MATRIX_WIDTH, MATRIX_HEIGHT > 255 ? 255 : MATRIX_HEIGHT, blurAmount);
|
blur2d(effects.leds, MATRIX_WIDTH > 255 ? 255 : MATRIX_WIDTH, MATRIX_HEIGHT > 255 ? 255 : MATRIX_HEIGHT, blurAmount);
|
||||||
|
|
|
@ -44,8 +44,8 @@ MatrixPanel_I2S_DMA dma_display;
|
||||||
// End of default setup for RGB Matrix 64x32 panel
|
// End of default setup for RGB Matrix 64x32 panel
|
||||||
///////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
int time_counter = 0;
|
uint16_t time_counter = 0, cycles = 0, fps = 0;
|
||||||
int cycles = 0;
|
unsigned long fps_timer;
|
||||||
|
|
||||||
CRGB currentColor;
|
CRGB currentColor;
|
||||||
CRGBPalette16 palettes[] = {HeatColors_p, LavaColors_p, RainbowColors_p, RainbowStripeColors_p, CloudColors_p};
|
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.
|
// Panels are the same - some seem to display ghosting artefacts at lower brightness levels.
|
||||||
// In the setup() function do something like:
|
// 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
|
* 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);
|
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(){
|
void loop(){
|
||||||
|
@ -85,12 +100,21 @@ void loop(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
time_counter += 1;
|
++time_counter;
|
||||||
cycles++;
|
++cycles;
|
||||||
|
++fps;
|
||||||
|
|
||||||
if (cycles >= 1024) {
|
if (cycles >= 1024) {
|
||||||
time_counter = 0;
|
time_counter = 0;
|
||||||
cycles = 0;
|
cycles = 0;
|
||||||
currentPalette = palettes[random(0,sizeof(palettes)/sizeof(palettes[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;
|
||||||
|
}
|
||||||
}
|
}
|
25
fillrate.md
Normal file
25
fillrate.md
Normal file
|
@ -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...
|
Loading…
Reference in a new issue