Files
Clément SAILLANT 9197d399ea add comment
2025-02-12 15:29:31 +01:00

1542 lines
60 KiB
C++

/* ObjectFLED - Teensy 4.x DMA to all pins for independent control of large and
multiple LED display objects
Copyright (c) 2024 Kurt Funderburg
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
OctoWS2811 library code was well-studied and substantial portions of it
used to implement high-speed, non-blocking DMA for LED signal output in this
library. See below for a summary of changes made to the original OctoWS2811.
OctoWS2811 - High Performance WS2811 LED Display Library
Copyright (c) 2013 Paul Stoffregen, PJRC.COM, LLC
http://www.pjrc.com/teensy/td_libs_OctoWS2811.html
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/
/*
Teensy 4.0 pin - port assignments
GPIO6List = { 0, 1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 }
//12 top, 4 bottom GPIO7List = { 6, 7, 8, 9, 10, 11, 12, 13, 32 } //8 top, 1
bottom GPIO8List = { 28, 30, 31, 34, 35, 36, 37, 38, 39 } //0 top, 9 bottom
GOIO9List = { 2, 3, 4, 5, 29, 33 } //4 top, 2 bottom
Teensy 4.1 pin - port assignments
GPIO6List = { 0, 1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 38,
39, 40, 41 } //20 top, 0 bottom GPIO7List = { 6, 7, 8, 9, 10, 11, 12, 13, 32,
34, 35, 36, 37 } //13 top, 0 bottom GPIO8List = { 28, 30, 31, 42, 43, 44, 45,
46, 47 } //3 top, 6 bottom GOIO9List = { 2, 3, 4, 5, 29, 33, 48, 49, 50, 51,
52, 53, 54 } //6 top, 7 bottom
* 4 pin groups, 4 timer groups, !4 dma channel groups: only 1 DMA group (4 ch)
maps to GPIO (DMAMUX
* mapping) Also, only DMA ch0-3 have periodic mode for timer trigger (p77
manual). Separate objects
* cannot DMA at once.
*
* CHANGES:
* Moved some variables so class supports multiple instances with separate LED
config params
* Implemented context switching so multiple instances can show() independently
* Re-parameterized TH_TL, T0H, T1H, OC_FACTOR; recalc time for latch at end of
show()
* Added genFrameBuffer() to implement RGB order, brightness, color balance, and
serpentine
* Added setBrightness(), setBalance()
* FrameBuffer no longer passed in, constructor now creates buffer; destructor
added
* Added support for per-object setting of OC factor, TH+TL, T0H, T1H, and
LATCH_DELAY in begin function
* Set DSE=3, SPEED=0, SRE=0 on output pins per experiment & PJRC forum guidance
* New default values for TH_TL, T0H, T1H, LATCH_DELAY to work with Audio lib and
more LED types
* Added wait for prior xmission to complete in destructor
* CHANGES by Clément SAILLANT:
* Added setPixel method
* Added support for GRBW LED strips
*/
#ifndef __IMXRT1062__
#error Only Teensy 4.x supported.
#else
#include "ObjectFLED.h"
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
volatile uint32_t framebuffer_index = 0; // isr()
uint8_t *ObjectFLED::frameBuffer; // isr()
uint32_t ObjectFLED::numbytes; // isr()
uint8_t ObjectFLED::numpins; // isr()
uint8_t ObjectFLED::pin_bitnum[NUM_DIGITAL_PINS]; // isr()
uint8_t ObjectFLED::pin_offset[NUM_DIGITAL_PINS]; // isr()
uint32_t ObjectFLED::bitdata[BYTES_PER_DMA * 64]
__attribute__((used, aligned(32))); // isr()
uint32_t ObjectFLED::bitmask[4] __attribute__((used, aligned(32)));
DMASetting ObjectFLED::dma2next;
DMAChannel ObjectFLED::dma1;
DMAChannel ObjectFLED::dma2;
DMAChannel ObjectFLED::dma3;
volatile bool dma_first;
ObjectFLED::ObjectFLED(uint16_t numLEDs, void *drawBuf, uint8_t config,
uint8_t numPins, const uint8_t *pinList,
uint8_t serpentine) {
serpNumber = serpentine;
drawBuffer = drawBuf;
params = config;
if (numPins > NUM_DIGITAL_PINS)
numPins = NUM_DIGITAL_PINS;
numpins = numPins; // static/isr
stripLen = numLEDs / numpins;
memcpy(pinlist, pinList, numpins);
if ((params & 0x3F) < 6) {
frameBuffer = new uint8_t[numLEDs * 3]; // static/isr
numbytes = stripLen * 3; // RGB formats //static/isr
} else {
frameBuffer = new uint8_t[numLEDs * 4]; // static/isr
numbytes = stripLen * 4; // RGBW formats //static/isr
}
numpinsLocal = numPins;
frameBufferLocal = frameBuffer;
numbytesLocal = numbytes;
} // ObjectFLED constructor
extern "C" void xbar_connect(unsigned int input,
unsigned int output); // in pwm.c
static volatile uint32_t *standard_gpio_addr(volatile uint32_t *fastgpio) {
return (volatile uint32_t *)((uint32_t)fastgpio - 0x01E48000);
}
void ObjectFLED::begin(uint16_t latchDelay) {
LATCH_DELAY = latchDelay;
begin();
}
void ObjectFLED::begin(double OCF, uint16_t latchDelay) {
OC_FACTOR = (float)OCF;
LATCH_DELAY = latchDelay;
begin();
}
void ObjectFLED::begin(uint16_t period, uint16_t t0h, uint16_t t1h,
uint16_t latchDelay) {
TH_TL = period;
T0H = t0h;
T1H = t1h;
LATCH_DELAY = latchDelay;
begin();
}
// INPUT stripLen, frameBuffer, params, numPins, pinList
// GPIOR bits set for pins[i] -> bitmask, pin_bitnum[i], pin_offset[i]
// init timers, xbar to DMA, DMA bitdata -> GPIOR; clears frameBuffer (total
// LEDs * 3 bytes)
void ObjectFLED::begin(void) {
numpins = numpinsLocal; // needed to compute pin mask/offset & bitmask since
// static for isr
// Set each pin's bitmask bit, store offset & bit# for pin
memset(bitmask, 0, sizeof(bitmask));
for (uint32_t i = 0; i < numpins; i++) {
uint8_t pin = pinlist[i];
if (pin >= NUM_DIGITAL_PINS)
continue; // ignore illegal pins
uint8_t bit = digitalPinToBit(pin); // pin's bit index in word port DR
// which GPIO R controls this pin: 0-3 map to GPIO6-9 then map to DMA compat
// GPIO1-4
uint8_t offset =
((uint32_t)portOutputRegister(pin) - (uint32_t)&GPIO6_DR) >> 14;
if (offset > 3)
continue; // ignore unknown pins
pin_bitnum[i] = bit; // static/isr
pin_offset[i] = offset; // static/isr
uint32_t mask = 1 << bit; // mask32 = bit set @position in GPIO DR
bitmask[offset] |=
mask; // bitmask32[0..3] = collective pin bit masks for each GPIO DR
// bit7:6 SPEED; bit 5:3 DSE; bit0 SRE (default SPEED = 0b10; def. DSE =
// 0b110)
*portControlRegister(pin) &= ~0xF9; // clear SPEED, DSE, SRE
*portControlRegister(pin) |=
((OUTPUT_PAD_SPEED & 0x3) << 6) |
((OUTPUT_PAD_DSE & 0x7) << 3); // DSE = 0b011 for LED overclock
// clear pin bit in IOMUX_GPR26 to map GPIO6-9 to GPIO1-4 for DMA
*(&IOMUXC_GPR_GPR26 + offset) &= ~mask;
*standard_gpio_addr(portModeRegister(pin)) |=
mask; // GDIR? bit flag set output mode
}
// stash context for multi-show
memcpy(bitmaskLocal, bitmask, 16);
memcpy(pin_bitnumLocal, pin_bitnum, numpins);
memcpy(pin_offsetLocal, pin_offset, numpins);
arm_dcache_flush_delete(bitmask,
sizeof(bitmask)); // can't DMA from cached memory
// Set up 3 timers to create waveform timing events
comp1load[0] =
(uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)TH_TL / OC_FACTOR);
comp1load[1] =
(uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T0H / OC_FACTOR);
comp1load[2] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T1H /
(1.0 + ((OC_FACTOR - 1.0) / 3)));
TMR4_ENBL &= ~7;
TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR;
TMR4_CSCTRL0 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN;
TMR4_CNTR0 = 0;
TMR4_LOAD0 = 0;
TMR4_COMP10 = comp1load[0];
TMR4_CMPLD10 = comp1load[0];
TMR4_CTRL0 =
TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(3);
TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
TMR4_CNTR1 = 0;
TMR4_LOAD1 = 0;
TMR4_COMP11 = comp1load[1]; // T0H
TMR4_CMPLD11 = comp1load[1];
TMR4_CTRL1 =
TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3);
TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
TMR4_CNTR2 = 0;
TMR4_LOAD2 = 0;
TMR4_COMP12 = comp1load[2]; // T1H
TMR4_CMPLD12 = comp1load[2];
TMR4_CTRL2 =
TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3);
// route the timer outputs through XBAR to edge trigger DMA request: only 4
// mappings avail.
CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON);
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_DMA_CH_MUX_REQ30);
xbar_connect(XBARA1_IN_QTIMER4_TIMER1, XBARA1_OUT_DMA_CH_MUX_REQ31);
xbar_connect(XBARA1_IN_QTIMER4_TIMER2, XBARA1_OUT_DMA_CH_MUX_REQ94);
XBARA1_CTRL0 = XBARA_CTRL_STS1 | XBARA_CTRL_EDGE1(3) | XBARA_CTRL_DEN1 |
XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0;
XBARA1_CTRL1 = XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0;
// configure DMA channels
dma1.begin();
dma1.TCD->SADDR = bitmask; // source 4*32b GPIO pin mask
dma1.TCD->SOFF = 8; // bytes offset added to SADDR after each transfer
// SMOD(4) low bits of SADDR to update with adds of SOFF
// SSIZE(3) code for 64 bit transfer size DSIZE(2) code for 32 bit transfer
// size
dma1.TCD->ATTR =
DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2);
dma1.TCD->NBYTES_MLOFFYES =
DMA_TCD_NBYTES_DMLOE | // Dest minor loop offsetting enable
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
DMA_TCD_NBYTES_MLOFFYES_NBYTES(
16); // #bytes to tansfer, offsetting enabled
dma1.TCD->SLAST = 0; // add to SADDR after xfer
dma1.TCD->DADDR = &GPIO1_DR_SET;
dma1.TCD->DOFF = 16384; //&GPIO1_DR_SET + DOFF = next &GPIO2_DR_SET
dma1.TCD->CITER_ELINKNO =
numbytes *
8; // CITER outer loop count (linking disabled) = # LED bits to write
dma1.TCD->DLASTSGA = -65536; // add to DADDR after xfer
dma1.TCD->BITER_ELINKNO =
numbytes * 8; // Beginning CITER (not decremented by transfer)
dma1.TCD->CSR =
DMA_TCD_CSR_DREQ; // channel ERQ field cleared when minor loop completed
dma1.triggerAtHardwareEvent(
DMAMUX_SOURCE_XBAR1_0); // only 4 XBAR1 triggers (DMA MUX mapping)
dma2next.TCD->SADDR = bitdata; // uint32_t bitdata[BYTES_PER_DMA*64]
dma2next.TCD->SOFF = 8;
dma2next.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_DSIZE(2);
dma2next.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE |
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
DMA_TCD_NBYTES_MLOFFYES_NBYTES(16);
dma2next.TCD->SLAST = 0;
dma2next.TCD->DADDR = &GPIO1_DR_CLEAR;
dma2next.TCD->DOFF = 16384;
dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
dma2next.TCD->DLASTSGA = (int32_t)(dma2next.TCD);
dma2next.TCD->BITER_ELINKNO = BYTES_PER_DMA * 8;
dma2next.TCD->CSR = 0;
dma2.begin();
dma2 = dma2next; // copies TCD
dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_1);
dma2.attachInterrupt(isr);
dma3.begin();
dma3.TCD->SADDR = bitmask;
dma3.TCD->SOFF = 8;
dma3.TCD->ATTR =
DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2);
dma3.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE |
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
DMA_TCD_NBYTES_MLOFFYES_NBYTES(16);
dma3.TCD->SLAST = 0;
dma3.TCD->DADDR = &GPIO1_DR_CLEAR;
dma3.TCD->DOFF = 16384;
dma3.TCD->CITER_ELINKNO = numbytes * 8;
dma3.TCD->DLASTSGA = -65536;
dma3.TCD->BITER_ELINKNO = numbytes * 8;
dma3.TCD->CSR = DMA_TCD_CSR_DREQ | DMA_TCD_CSR_DONE;
dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_2);
} // begin()
//*dest = *bitdata + pin offset
//*pixels = pin's block in frameBuffer
// mask = pin's bit position in GPIOR
// set a pin's mask32 for each color bit=0 at every 4*words32 in bitdata+offset
void fillbits(uint32_t *dest, const uint8_t *pixels, int n, uint32_t mask) {
do {
uint8_t pix = *pixels++;
if (!(pix & 0x80))
*dest |= mask;
dest += 4;
if (!(pix & 0x40))
*dest |= mask;
dest += 4;
if (!(pix & 0x20))
*dest |= mask;
dest += 4;
if (!(pix & 0x10))
*dest |= mask;
dest += 4;
if (!(pix & 0x08))
*dest |= mask;
dest += 4;
if (!(pix & 0x04))
*dest |= mask;
dest += 4;
if (!(pix & 0x02))
*dest |= mask;
dest += 4;
if (!(pix & 0x01))
*dest |= mask;
dest += 4;
} while (--n > 0);
}
void ObjectFLED::genFrameBuffer(uint32_t serp) {
uint32_t j = 0;
int jChange = -3;
if (serp == 0) { // use faster loops if no serp
switch (params & 0x3F) {
case CORDER_RGBW: // R,G,B = R,G,B - MIN(R,G,B); W = MIN(R,G,B)
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
} // for(leds in drawbuffer)
break;
case CORDER_GRBW:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_BGRW:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_RBGW:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_GBRW:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_BRGW:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_WRGB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 2) * gLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 3) * bLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_WRBG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_WGRB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * rLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_WGBR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * rLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_WBRG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * gLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_WBGR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * gLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_RWGB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_RWBG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_GWRB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j + 2) * rLevel / 65025,
*((uint8_t *)drawBuffer + j) * gLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 3) * bLevel / 65025);
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * rLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_GWBR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * rLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_BWRG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j + 2) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 3) * gLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j) * bLevel / 65025);
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * gLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_BWGR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * gLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_RGWB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_RBWG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 1) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_GRWB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_GBWR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 3) * bLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_BRWG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_BGWR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
}
break;
case CORDER_GBR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
*(frameBuffer + i + 2) = *((uint8_t *)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i) = *((uint8_t *)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025;
j += 3;
} // for(leds in drawbuffer)
break;
case CORDER_BGR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
*(frameBuffer + i + 2) = *((uint8_t *)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i) = *((uint8_t *)drawBuffer + j + 2) * bLevel / 65025;
j += 3;
} // for(leds in drawbuffer)
break;
case CORDER_BRG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
*(frameBuffer + i + 1) = *((uint8_t *)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i) = *((uint8_t *)drawBuffer + j + 2) * bLevel / 65025;
j += 3;
} // for(leds in drawbuffer)
break;
case CORDER_GRB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
*(frameBuffer + i + 1) = *((uint8_t *)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i) = *((uint8_t *)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025;
j += 3;
} // for(leds in drawbuffer)
break;
case CORDER_RGB:
default:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
*(frameBuffer + i) = *((uint8_t *)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025;
j += 3;
} // for(leds in drawbuffer)
} // switch()
} else { // serpentine
switch (params & 0x3F) {
case CORDER_RGBW: // R,G,B = R,G,B - MIN(R,G,B); W = MIN(R,G,B)
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 3;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 3;
jChange = -3;
}
}
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
} // for(leds in drawbuffer)
break;
case CORDER_GRBW:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_BGRW:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_RBGW:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_GBRW:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_BRGW:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_WRGB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_WRBG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_WGRB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * rLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_WGBR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * rLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_WBRG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * gLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_WBGR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * gLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j + 1) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_RWGB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_RWBG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_GWRB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * rLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_GWBR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * rLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_BWRG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * gLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_BGWR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
if (i % (serp * 4) == 0) {
if (jChange < 0) {
j = i / 4 * 4;
jChange = 3;
} else {
j = (i / 4 + serp - 1) * 4;
jChange = -3;
}
}
uint8_t minRGB = MIN(*((uint8_t *)drawBuffer + j + 3) * rLevel / 65025,
*((uint8_t *)drawBuffer + j + 2) * gLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t *)drawBuffer + j) * bLevel / 65025);
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 3) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * gLevel / 65025 - minRGB;
*(frameBuffer + i) =
*((uint8_t *)drawBuffer + j) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
}
break;
case CORDER_GBR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
if (i % (serp * 3) == 0) {
if (jChange < 0) {
j = i / 3 * 3;
jChange = 3;
} else {
j = (i / 3 + serp - 1) * 3;
jChange = -3;
}
}
*(frameBuffer + i + 2) = *((uint8_t *)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i) = *((uint8_t *)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025;
j += jChange;
} // for(leds in drawbuffer)
break;
case CORDER_BGR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
if (i % (serp * 3) == 0) {
if (jChange < 0) {
j = i / 3 * 3;
jChange = 3;
} else {
j = (i / 3 + serp - 1) * 3;
jChange = -3;
}
}
*(frameBuffer + i + 2) = *((uint8_t *)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i) = *((uint8_t *)drawBuffer + j + 2) * bLevel / 65025;
j += jChange;
} // for(leds in drawbuffer)
break;
case CORDER_BRG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
if (i % (serp * 3) == 0) {
if (jChange < 0) {
j = i / 3 * 3;
jChange = 3;
} else {
j = (i / 3 + serp - 1) * 3;
jChange = -3;
}
}
*(frameBuffer + i + 1) = *((uint8_t *)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i) = *((uint8_t *)drawBuffer + j + 2) * bLevel / 65025;
j += jChange;
} // for(leds in drawbuffer)
break;
case CORDER_GRB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
if (i % (serp * 3) == 0) {
if (jChange < 0) {
j = i / 3 * 3;
jChange = 3;
} else {
j = (i / 3 + serp - 1) * 3;
jChange = -3;
}
}
*(frameBuffer + i + 1) = *((uint8_t *)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i) = *((uint8_t *)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025;
j += jChange;
} // for(leds in drawbuffer)
break;
case CORDER_RGB:
default:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
if (i % (serp * 3) == 0) {
if (jChange < 0) {
j = i / 3 * 3;
jChange = 3;
} else {
j = (i / 3 + serp - 1) * 3;
jChange = -3;
}
}
*(frameBuffer + i) = *((uint8_t *)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i + 1) =
*((uint8_t *)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i + 2) =
*((uint8_t *)drawBuffer + j + 2) * bLevel / 65025;
j += jChange;
} // for(leds in drawbuffer)
} // switch()
} // else serpentine
} // genFrameBuffer()
// pre-show prior transfer wait, copies drawBuffer -> frameBuffer
// resets timers, clears pending DMA reqs
// fills bitdata[BYTES_PER_DMA * 64 * 4 bytes] from frameBuffer with 4-block
// bitmasks for 0's in led data 4 word32s for each bit in (led data)/pin = 16 *
// 8 = 96 bitdata bytes for each LED byte: 288 bytes / LED launches DMA with IRQ
// activation to reload bitdata from frameBuffer
void ObjectFLED::show(void) {
while (!dma3.complete())
; // wait for dma to complete before reset/re-use
// Restore context if needed
if (frameBuffer != frameBufferLocal) {
numpins = numpinsLocal;
frameBuffer = frameBufferLocal;
numbytes = numbytesLocal;
memcpy(bitmask, bitmaskLocal, 16);
memcpy(pin_bitnum, pin_bitnumLocal, numpins);
memcpy(pin_offset, pin_offsetLocal, numpins);
arm_dcache_flush_delete(bitmask,
sizeof(bitmask)); // can't DMA from cached memory
// Restore 3 timers to create waveform timing events
TMR4_COMP10 = comp1load[0];
TMR4_CMPLD10 = comp1load[0];
TMR4_COMP11 = comp1load[1]; // T0H
TMR4_CMPLD11 = comp1load[1];
TMR4_COMP12 = comp1load[2]; // T1H
TMR4_CMPLD12 = comp1load[2];
// restore DMA loop control
dma1.TCD->CITER_ELINKNO =
numbytes *
8; // CITER outer loop count (linking disabled) = # LED bits to write
dma1.TCD->BITER_ELINKNO =
numbytes * 8; // Beginning CITER (not decremented by transfer)
dma3.TCD->CITER_ELINKNO = numbytes * 8;
dma3.TCD->BITER_ELINKNO = numbytes * 8;
} // done restoring context
genFrameBuffer(serpNumber);
// disable timers
uint16_t enable = TMR4_ENBL;
TMR4_ENBL = enable & ~7;
// force all timer outputs to logic low
TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR;
TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
// clear any prior pending DMA requests
XBARA1_CTRL0 |= XBARA_CTRL_STS1 | XBARA_CTRL_STS0;
XBARA1_CTRL1 |= XBARA_CTRL_STS0;
// fill the DMA transmit buffer
memset(bitdata, 0, sizeof(bitdata)); // BYTES_PER_DMA * 64 words32
uint32_t count = numbytes; // bytes per strip
if (count > BYTES_PER_DMA * 2)
count = BYTES_PER_DMA * 2;
framebuffer_index = count; // ptr to framebuffer last byte output
// Sets each pin mask in bitdata32[BYTES_PER_DMA*64] for every 0 bit of pin's
// frameBuffer block bytes
for (uint32_t i = 0; i < numpins; i++) { // for each pin
fillbits(bitdata + pin_offset[i], (uint8_t *)frameBuffer + i * numbytes,
count, 1 << pin_bitnum[i]);
}
arm_dcache_flush_delete(bitdata,
count * 128); // don't need bitdata in cache for DMA
// set up DMA transfers
if (numbytes <= BYTES_PER_DMA * 2) {
dma2.TCD->SADDR = bitdata;
dma2.TCD->DADDR = &GPIO1_DR_CLEAR;
dma2.TCD->CITER_ELINKNO = count * 8;
dma2.TCD->CSR = DMA_TCD_CSR_DREQ;
} else {
dma2.TCD->SADDR = bitdata;
dma2.TCD->DADDR = &GPIO1_DR_CLEAR;
dma2.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
dma2.TCD->CSR = 0;
dma2.TCD->CSR = DMA_TCD_CSR_INTMAJOR | DMA_TCD_CSR_ESG;
dma2next.TCD->SADDR = bitdata + BYTES_PER_DMA * 32;
dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
if (numbytes <= BYTES_PER_DMA * 3) {
dma2next.TCD->CSR = DMA_TCD_CSR_ESG;
} else {
dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR;
}
dma_first = true;
}
dma3.clearComplete();
dma1.enable();
dma2.enable();
dma3.enable();
// initialize timers
TMR4_CNTR0 = 0;
TMR4_CNTR1 = comp1load[0] + 1;
TMR4_CNTR2 = comp1load[0] + 1;
// wait for last LED reset to finish
while (micros() - update_begin_micros <
numbytes * 8 * TH_TL / OC_FACTOR / 1000 + LATCH_DELAY)
;
// start everything running!
TMR4_ENBL = enable | 7;
update_begin_micros = micros();
} // show()
// INPUT: dma2, dma2next, bitdata, framebuffer_inedex, numpins, numbytes,
// pin_offset[], pin_bitnum[] Reads next block of framebuffer -> fillbits() ->
// bitdata Checks for last block to transfer, next to last, or not to update
// dma2next major loop
void ObjectFLED::isr(void) {
// first ack the interrupt
dma2.clearInterrupt();
// fill (up to) half the transmit buffer with new fillbits(frameBuffer data)
// digitalWriteFast(12, HIGH);
uint32_t *dest;
if (dma_first) {
dma_first = false;
dest = bitdata;
} else {
dma_first = true;
dest = bitdata + BYTES_PER_DMA * 32;
}
memset(dest, 0, sizeof(bitdata) / 2);
uint32_t index = framebuffer_index;
uint32_t count = numbytes - framebuffer_index;
if (count > BYTES_PER_DMA)
count = BYTES_PER_DMA;
framebuffer_index = index + count;
for (int i = 0; i < numpins; i++) {
fillbits(dest + pin_offset[i],
(uint8_t *)frameBuffer + index + i * numbytes, count,
1 << pin_bitnum[i]);
}
arm_dcache_flush_delete(dest, count * 128);
// digitalWriteFast(12, LOW);
// queue it for the next DMA transfer
dma2next.TCD->SADDR = dest;
dma2next.TCD->CITER_ELINKNO = count * 8;
uint32_t remain = numbytes - (index + count);
if (remain == 0) {
dma2next.TCD->CSR = DMA_TCD_CSR_DREQ;
} else if (remain <= BYTES_PER_DMA) {
dma2next.TCD->CSR = DMA_TCD_CSR_ESG;
} else {
dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR;
}
} // isr()
int ObjectFLED::busy(void) {
if (micros() - update_begin_micros <
numbytes * TH_TL / OC_FACTOR / 1000 * 8 + LATCH_DELAY) {
return 1;
}
return 0;
}
void ObjectFLED::setBrightness(uint8_t brightLevel) {
brightness = brightLevel;
rLevel = brightness * (colorBalance >> 16);
gLevel = brightness * ((colorBalance >> 8) & 0xFF);
bLevel = brightness * (colorBalance & 0xFF);
}
void ObjectFLED::setBalance(uint32_t balMask) {
colorBalance = balMask & 0xFFFFFF;
rLevel = brightness * (colorBalance >> 16);
gLevel = brightness * ((colorBalance >> 8) & 0xFF);
bLevel = brightness * (colorBalance & 0xFF);
}
// Fades CRGB array towards the background color by amount.
void fadeToColorBy(void *leds, uint16_t count, uint32_t color,
uint8_t fadeAmt) {
for (uint32_t x = 0; x < count * 3; x += 3) {
// fade red
*((uint8_t *)leds + x) =
((*((uint8_t *)leds + x) * (1 + (255 - fadeAmt))) >> 8) +
((((color >> 16) & 0xFF) * (1 + fadeAmt)) >> 8);
// fade green
*((uint8_t *)leds + x + 1) =
((*((uint8_t *)leds + x + 1) * (1 + (255 - fadeAmt))) >> 8) +
((((color >> 8) & 0xFF) * (1 + fadeAmt)) >> 8);
// fade blue
*((uint8_t *)leds + x + 2) =
((*((uint8_t *)leds + x + 2) * (1 + (255 - fadeAmt))) >> 8) +
(((color & 0xFF) * (1 + fadeAmt)) >> 8);
}
} // fadeToColorBy()
// Safely draws box even if partially offscreen on 2D CRGB array
void drawSquare(void *leds, uint16_t planeY, uint16_t planeX, int yCorner,
int xCorner, uint32_t size, uint32_t color) {
if (size != 0) {
size--;
} else {
return;
}
for (int x = xCorner; x <= xCorner + (int)size; x++) {
// if validX { if validY+S {draw Y+S,X}; if validY {draw Y, X} }
if ((x >= 0) && (x < planeX)) { // valid X
if ((yCorner >= 0) && (yCorner < planeY)) {
*((uint8_t *)leds + (yCorner * planeX + x) * 3) =
((color >> 16) & 0xFF);
*((uint8_t *)leds + (yCorner * planeX + x) * 3 + 1) =
((color >> 8) & 0xFF);
*((uint8_t *)leds + (yCorner * planeX + x) * 3 + 2) = (color & 0xFF);
}
if ((yCorner + size >= 0) && (yCorner + size < planeY)) {
*((uint8_t *)leds + ((yCorner + size) * planeX + x) * 3) =
((color >> 16) & 0xFF);
*((uint8_t *)leds + ((yCorner + size) * planeX + x) * 3 + 1) =
((color >> 8) & 0xFF);
*((uint8_t *)leds + ((yCorner + size) * planeX + x) * 3 + 2) =
(color & 0xFF);
}
} // if valid x
} // for x
for (int y = yCorner; y <= yCorner + (int)size; y++) {
if ((y >= 0) && (y < planeY)) { // valid y
if ((xCorner >= 0) && (xCorner < planeX)) {
*((uint8_t *)leds + (xCorner + y * planeX) * 3) =
((color >> 16) & 0xFF);
*((uint8_t *)leds + (xCorner + y * planeX) * 3 + 1) =
((color >> 8) & 0xFF);
*((uint8_t *)leds + (xCorner + y * planeX) * 3 + 2) = (color & 0xFF);
}
if ((xCorner + size >= 0) && (xCorner + size < planeX)) {
*((uint8_t *)leds + (xCorner + size + y * planeX) * 3) =
((color >> 16) & 0xFF);
*((uint8_t *)leds + (xCorner + size + y * planeX) * 3 + 1) =
((color >> 8) & 0xFF);
*((uint8_t *)leds + (xCorner + size + y * planeX) * 3 + 2) =
(color & 0xFF);
}
} // if valid y
} // for y
} // drawSquare()
// set one pixel in CRGB array
void ObjectFLED::setPixel(uint32_t index, uint8_t r, uint8_t g, uint8_t b,
uint8_t w) {
if (index < numbytes / 4) {
frameBuffer[index * 4] = r;
frameBuffer[index * 4 + 1] = g;
frameBuffer[index * 4 + 2] = b;
frameBuffer[index * 4 + 3] = w;
}
}
// get one pixel in CRGB array
void ObjectFLED::getPixel(uint32_t index, uint8_t &r, uint8_t &g, uint8_t &b,
uint8_t &w) {
if (index < numbytes / 4) {
r = frameBuffer[index * 4];
g = frameBuffer[index * 4 + 1];
b = frameBuffer[index * 4 + 2];
w = frameBuffer[index * 4 + 3];
}
}
#endif // __IMXRT1062__