ESP32-HUB75-MatrixPanel-DMA/ESP32-RGB64x32MatrixPanel-I2S-DMA.cpp

576 lines
23 KiB
C++
Raw Normal View History

2018-10-23 02:00:47 +02:00
#include "ESP32-RGB64x32MatrixPanel-I2S-DMA.h"
// Credits: Louis Beaudoin <https://github.com/pixelmatix/SmartMatrix/tree/teensylc>
// and Sprite_TM: https://www.esp32.com/viewtopic.php?f=17&t=3188 and https://www.esp32.com/viewtopic.php?f=13&t=3256
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<COLOR_DEPTH_BITS; i++) {
numDescriptorsPerRow += 1<<(i - lsbMsbTransitionBit - 1);
}
int ramrequired = numDescriptorsPerRow * ROWS_PER_FRAME * ESP32_NUM_FRAME_BUFFERS * sizeof(lldesc_t);
ramrequired += 100000; // HACK Hard Coded: Keep at least 100k free!
int largestblockfree = heap_caps_get_largest_free_block(MALLOC_CAP_DMA);
Serial.printf("lsbMsbTransitionBit of %d requires %d RAM, %d available, leaving %d free: \r\n", lsbMsbTransitionBit, ramrequired, largestblockfree, largestblockfree - ramrequired);
if(ramrequired < (largestblockfree))
break;
if(lsbMsbTransitionBit < COLOR_DEPTH_BITS - 1)
lsbMsbTransitionBit++;
else
break;
}
if(numDescriptorsPerRow * ROWS_PER_FRAME * ESP32_NUM_FRAME_BUFFERS * sizeof(lldesc_t) > 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<COLOR_DEPTH_BITS; i++)
nsPerRow += (1<<(i - lsbMsbTransitionBit - 1)) * (COLOR_DEPTH_BITS - i) * nsPerLatch;
//Serial.printf("nsPerRow: %d: \r\n", nsPerRow);
int nsPerFrame = nsPerRow * ROWS_PER_FRAME;
Serial.printf("nsPerFrame: %d: \r\n", nsPerFrame);
int actualRefreshRate = 1000000000UL/(nsPerFrame);
refreshRate = actualRefreshRate;
Serial.printf("lsbMsbTransitionBit of %d gives %d Hz refresh: \r\n", lsbMsbTransitionBit, actualRefreshRate);
2018-10-26 00:49:41 +02:00
if (actualRefreshRate > 250) // HACK Hard Coded: Minimum frame rate of 160
2018-10-23 02:00:47 +02:00
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; i<COLOR_DEPTH_BITS; i++) {
numDescriptorsPerRow += 1<<(i - lsbMsbTransitionBit - 1);
}
Serial.printf("Descriptors for lsbMsbTransitionBit %d/%d with %d rows require %d bytes of DMA RAM\r\n", lsbMsbTransitionBit, COLOR_DEPTH_BITS - 1, ROWS_PER_FRAME, 2 * numDescriptorsPerRow * ROWS_PER_FRAME * sizeof(lldesc_t));
// malloc the DMA linked list descriptors that i2s_parallel will need
int desccount = numDescriptorsPerRow * ROWS_PER_FRAME;
lldesc_t * dmadesc_a = (lldesc_t *)heap_caps_malloc(desccount * sizeof(lldesc_t), MALLOC_CAP_DMA);
assert("Can't allocate descriptor buffer a");
if(!dmadesc_a) {
Serial.printf("Could not malloc descriptor buffer a.");
return;
}
lldesc_t * dmadesc_b = (lldesc_t *)heap_caps_malloc(desccount * sizeof(lldesc_t), MALLOC_CAP_DMA);
assert("Could not malloc descriptor buffer b.");
if(!dmadesc_b) {
Serial.printf("can't malloc");
return;
}
Serial.printf("SmartMatrix Mallocs Complete\r\n");
Serial.printf("Heap Memory Available: %d bytes total, %d bytes largest free block: \r\n", heap_caps_get_free_size(0), heap_caps_get_largest_free_block(0));
Serial.printf("8-bit Accessible Memory Available: %d bytes total, %d bytes largest free block: \r\n", heap_caps_get_free_size(MALLOC_CAP_8BIT), heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
Serial.printf("32-bit Memory Available: %d bytes total, %d bytes largest free block: \r\n", heap_caps_get_free_size(MALLOC_CAP_32BIT), heap_caps_get_largest_free_block(MALLOC_CAP_32BIT));
Serial.printf("DMA Memory Available: %d bytes total, %d bytes largest free block: \r\n", heap_caps_get_free_size(MALLOC_CAP_DMA), heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
lldesc_t *prevdmadesca = 0;
lldesc_t *prevdmadescb = 0;
int currentDescOffset = 0;
// fill DMA linked lists for both frames
for(int j=0; j<ROWS_PER_FRAME; j++) {
// first set of data is LSB through MSB, single pass - all color bits are displayed once, which takes care of everything below and inlcluding LSBMSB_TRANSITION_BIT
// TODO: size must be less than DMA_MAX - worst case for SmartMatrix Library: 16-bpp with 256 pixels per row would exceed this, need to break into two
link_dma_desc(&dmadesc_a[currentDescOffset], prevdmadesca, &(matrixUpdateFrames[0].rowdata[j].rowbits[0].data), sizeof(rowBitStruct) * COLOR_DEPTH_BITS);
prevdmadesca = &dmadesc_a[currentDescOffset];
link_dma_desc(&dmadesc_b[currentDescOffset], prevdmadescb, &(matrixUpdateFrames[1].rowdata[j].rowbits[0].data), sizeof(rowBitStruct) * COLOR_DEPTH_BITS);
prevdmadescb = &dmadesc_b[currentDescOffset];
currentDescOffset++;
//Serial.printf("row %d: \r\n", j);
for(int i=lsbMsbTransitionBit + 1; i<COLOR_DEPTH_BITS; i++) {
// binary time division setup: we need 2 of bit (LSBMSB_TRANSITION_BIT + 1) four of (LSBMSB_TRANSITION_BIT + 2), etc
// because we sweep through to MSB each time, it divides the number of times we have to sweep in half (saving linked list RAM)
// we need 2^(i - LSBMSB_TRANSITION_BIT - 1) == 1 << (i - LSBMSB_TRANSITION_BIT - 1) passes from i to MSB
//Serial.printf("buffer %d: repeat %d times, size: %d, from %d - %d\r\n", nextBufdescIndex, 1<<(i - LSBMSB_TRANSITION_BIT - 1), (COLOR_DEPTH_BITS - i), i, COLOR_DEPTH_BITS-1);
for(int k=0; k < 1<<(i - lsbMsbTransitionBit - 1); k++) {
link_dma_desc(&dmadesc_a[currentDescOffset], prevdmadesca, &(matrixUpdateFrames[0].rowdata[j].rowbits[i].data), sizeof(rowBitStruct) * (COLOR_DEPTH_BITS - i));
prevdmadesca = &dmadesc_a[currentDescOffset];
link_dma_desc(&dmadesc_b[currentDescOffset], prevdmadescb, &(matrixUpdateFrames[1].rowdata[j].rowbits[i].data), sizeof(rowBitStruct) * (COLOR_DEPTH_BITS - i));
prevdmadescb = &dmadesc_b[currentDescOffset];
currentDescOffset++;
//Serial.printf("i %d, j %d, k %d\r\n", i, j, k);
}
}
}
//End markers
dmadesc_a[desccount-1].eof = 1;
dmadesc_b[desccount-1].eof = 1;
dmadesc_a[desccount-1].qe.stqe_next=(lldesc_t*)&dmadesc_a[0];
dmadesc_b[desccount-1].qe.stqe_next=(lldesc_t*)&dmadesc_b[0];
Serial.printf("Performing I2S setup.\n");
i2s_parallel_config_t cfg={
.gpio_bus={R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, LAT_PIN, OE_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, -1, -1, -1},
.gpio_clk=CLK_PIN,
.clkspeed_hz=ESP32_I2S_CLOCK_SPEED, //ESP32_I2S_CLOCK_SPEED, // formula used is 80000000L/(cfg->clkspeed_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-ordinates are within range, or it'll break everything big time.
if ( x_coord < 0 || y_coord < 0 || x_coord >= MATRIX_WIDTH || y_coord >= MATRIX_HEIGHT)
2018-10-23 02:00:47 +02:00
{
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<COLOR_DEPTH_BITS; color_depth_idx++) // color depth - 8 iterations
{
uint16_t mask = (1 << color_depth_idx); // 24 bit color
// The destination for the pixel bitstream
rowBitStruct *p = &matrixUpdateFrames[backbuf_id].rowdata[y_coord].rowbits[color_depth_idx]; //matrixUpdateFrames location to write to uint16_t's
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_LATCH-1) v|=BIT_LAT;
// 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; // ???
2018-10-23 02:00:47 +02:00
// 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; // ???
2018-10-23 02:00:47 +02:00
}
// 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
/*
// WORK IN PROGRESS
2018-10-26 00:49:41 +02:00
void RGB64x32MatrixPanel_I2S_DMA::writeRGB24Frame2DMABuffer(rgb_24 *framedata, int16_t frame_width = MATRIX_WIDTH, int16_t frame_height = MATRIX_HEIGHT)
{
if ( !dma_configuration_success)
assert("DMA configuration in begin() not performed or completed successfully.");
for (unsigned int y = 0; y < ROWS_PER_FRAME; y++) // half height - 16 iterations
{
unsigned char currentRow = y;
for(int j = 0; j < COLOR_DEPTH_BITS; j++) // color depth - 8 iterations
{
uint16_t mask = (1 << (j)); // 24 bit color
//MATRIX_DATA_STORAGE_TYPE *p=matrixUpdateFrames[backbuf_id].rowdata[y].rowbits[pl].data; //matrixUpdateFrames
rowBitStruct *p=&matrixUpdateFrames[backbuf_id].rowdata[currentRow].rowbits[j]; //matrixUpdateFrames location to write to
int i=0;
while(i < PIXELS_PER_LATCH) // row pixels (64) iterations
{
for(int k=0; k < MATRIX_WIDTH; k++) // row pixel width 64 iterations
{
int v=0; // the output bitstream
//#if (CLKS_DURING_LATCH == 0)
// if there is no latch to hold address, output ADDX lines directly to GPIO and latch data at end of cycle
int gpioRowAddress = currentRow;
// 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(j == 0)
gpioRowAddress = currentRow-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((i+k) == 0) v|=BIT_OE;
// drive latch while shifting out last bit of RGB data
if((i+k) == PIXELS_PER_LATCH-1) v|=BIT_LAT;
//#endif
// turn off OE after brightness value is reached when displaying MSBs
// MSBs always output normal brightness
// LSB (!j) outputs normal brightness as MSB from previous row is being displayed
if((j > 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 rgb_24 c1( 255,0,0);
struct rgb_24 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
*/
2018-10-23 02:00:47 +02:00
/*
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<MATRIX_HEIGHT/MATRIX_ROWS_IN_PARALLEL; y++) // half height - 16 iterations
{
unsigned char currentRow = y;
for(int j=0; j<COLOR_DEPTH_BITS; j++) // color depth - 8 iterations
{
uint16_t mask = (1 << (j)); // 24 bit color
//MATRIX_DATA_STORAGE_TYPE *p=matrixUpdateFrames[backbuf_id].rowdata[y].rowbits[pl].data; //matrixUpdateFrames
rowBitStruct *p=&matrixUpdateFrames[backbuf_id].rowdata[currentRow].rowbits[j]; //matrixUpdateFrames location to write to
int i=0;
while(i < PIXELS_PER_LATCH) // row pixels (64) iterations
{
for(int k=0; k < MATRIX_WIDTH; k++) // row pixel width 64 iterations
{
int v=0; // the output bitstream
//#if (CLKS_DURING_LATCH == 0)
// if there is no latch to hold address, output ADDX lines directly to GPIO and latch data at end of cycle
int gpioRowAddress = currentRow;
// 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(j == 0)
gpioRowAddress = currentRow-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((i+k) == 0) v|=BIT_OE;
// drive latch while shifting out last bit of RGB data
if((i+k) == PIXELS_PER_LATCH-1) v|=BIT_LAT;
//#endif
// turn off OE after brightness value is reached when displaying MSBs
// MSBs always output normal brightness
// LSB (!j) outputs normal brightness as MSB from previous row is being displayed
if((j > 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
*/