├── INSTALLATION.md ├── ObjectFLED.h ├── OjectFLED.cpp ├── README.md ├── RELEASE.md ├── examples ├── Cylon │ └── Cylon.ino ├── DemoReel2D │ └── DemoReel2D.ino ├── ObjectFLEDExperiment │ └── ObjectFLEDExperiment.ino ├── ObjectFLEDTest │ └── ObjectFLEDTest.ino ├── PlasmaAnimation │ └── PlasmaAnimation.ino ├── UsesFastLED │ └── UsesFastLED.ino └── UsesObjectFLED │ └── UsesObjectFLED.ino ├── keywords.txt └── library.properties /INSTALLATION.md: -------------------------------------------------------------------------------- 1 | The main branch is not always in a tested state. For installation, please download the [latest release](https://github.com/KurtMF/ObjectFLED/releases). Releases contain an ObjectFLED.zip file which is used to install in Arduino IDE: Sketch -> Include Library -> Add ZIP Library. -------------------------------------------------------------------------------- /ObjectFLED.h: -------------------------------------------------------------------------------- 1 | /* ObjectFLED - Teensy 4.x DMA to all pins for independent control of large and 2 | multiple LED display objects 3 | 4 | Copyright (c) 2024 Kurt Funderburg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | OctoWS2811 library code was well-studied and substantial portions of it used 17 | to implement high-speed, non-blocking DMA for LED signal output in this library. 18 | See ObjectFLED.cpp for a summary of changes made to the original OctoWS2811. 19 | 20 | OctoWS2811 - High Performance WS2811 LED Display Library 21 | Copyright (c) 2013 Paul Stoffregen, PJRC.COM, LLC 22 | http://www.pjrc.com/teensy/td_libs_OctoWS2811.html 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | THE SOFTWARE. 31 | */ 32 | 33 | #ifndef __IMXRT1062__ 34 | #error "Sorry, ObjectFLED only works on Teensy 4.x boards." 35 | #endif 36 | #if TEENSYDUINO < 121 37 | #error "Teensyduino version 1.21 or later is required to compile this library." 38 | #endif 39 | #ifndef ObjectFLED_h 40 | #define ObjectFLED_h 41 | #include 42 | #include "DMAChannel.h" 43 | 44 | //Experimentally found DSE=3, SPEED=0 gave best LED overclocking 45 | //boot defaults DSE=6, SPEED=2. 46 | #define OUTPUT_PAD_DSE 3 //Legal values 0-7 47 | #define OUTPUT_PAD_SPEED 0 //Legal values 0-3 48 | 49 | // Ordinary RGB data is converted to GPIO bitmasks on-the-fly using 50 | // a transmit buffer sized for 2 DMA transfers. The larger this setting, 51 | // the more interrupt latency OctoWS2811 can tolerate, but the transmit 52 | // buffer grows in size. For good performance, the buffer should be kept 53 | // smaller than the half the Cortex-M7 data cache. 54 | //bitdata[B_P_D * 64] buffer holds data (10KB) for 80 LED bytes: 4DW * 8b = 32DW/LEDB = 96DW/LED 55 | //framebuffer_index = B_P_D * 2 = pointer to next block for transfer (80 LEDB / bitdata buffer) 56 | #define BYTES_PER_DMA 20 //= number of pairs of LEDB (40=80B) bitmasks in bitdata. 57 | 58 | #define CORDER_RGB 0 //* WS2811, YF923 59 | #define CORDER_RBG 1 60 | #define CORDER_GRB 2 //* WS2811B, Most LED strips are wired this way 61 | #define CORDER_GBR 3 //* 62 | #define CORDER_BRG 4 //* Adafruit Product ID: 5984 As of November 5, 2024 - this strand has different 'internal' color ordering. It's now BRG not RGB, 63 | #define CORDER_BGR 5 //* Adafruit Dotstar LEDs SK9822 uses this CO but they use inverted start/stop bits 64 | #define CORDER_RGBW 6 //* Popular 65 | #define CORDER_RBGW 7 66 | #define CORDER_GRBW 8 67 | #define CORDER_GBRW 9 68 | #define CORDER_BRGW 10 69 | #define CORDER_BGRW 11 // Adafruit Dotstar LEDs SK9822 uses this CO but they use inverted start/stop bits 70 | #define CORDER_WRGB 12 71 | #define CORDER_WRBG 13 72 | #define CORDER_WGRB 14 73 | #define CORDER_WGBR 15 74 | #define CORDER_WBRG 16 75 | #define CORDER_WBGR 17 76 | #define CORDER_RWGB 18 77 | #define CORDER_RWBG 19 78 | #define CORDER_GWRB 20 79 | #define CORDER_GWBR 21 80 | #define CORDER_BWRG 22 81 | #define CORDER_BWGR 23 82 | #define CORDER_RGWB 24 83 | #define CORDER_RBWG 25 84 | #define CORDER_GRWB 26 85 | #define CORDER_GBWR 27 86 | #define CORDER_BRWG 28 87 | #define CORDER_BGWR 29 88 | 89 | 90 | //Usage: ObjectFLED myCube ( Num_LEDs, *drawBuffer, LED_type, numPins, *pinList, serpentineNumber ) 91 | class ObjectFLED { 92 | public: 93 | //Usage: ObjectFLED myCube ( Num_LEDs, *drawBuffer, LED_type, numPins, *pinList, serpentineNumber ) 94 | ObjectFLED(uint16_t numLEDs, void* drawBuf, uint8_t config, uint8_t numPins, const uint8_t* pinList, \ 95 | uint8_t serpentine = 0); 96 | 97 | ~ObjectFLED() { 98 | // Wait for prior xmission to end, don't need to wait for latch time before deleting buffer 99 | while (micros() - update_begin_micros < numbytes * 8 * TH_TL / OC_FACTOR / 1000 + 5); 100 | delete frameBuffer; 101 | } 102 | 103 | //begin() - Use defalut LED timing: 1.0 OC Factor, 1250 nS CLK (=800 KHz), 300 nS T0H, 750 nS T1H, 300 uS LED Latch Delay. 104 | void begin(void); 105 | 106 | //begin(LED_Latch_Delay_uS) - sets the LED Latch Delay. 107 | void begin(uint16_t); 108 | 109 | //begin(LED_Overclock_Factor, LED_Latch_Delay_uS) - divides default 1250 nS LED CLK (=800 KHz), 110 | // 300 nS T0H, 750 nS T1H; and optionally sets the LED Latch Delay. 111 | void begin(double, uint16_t = 300); 112 | 113 | //begin(LED_CLK_nS, LED_T0H_nS, LED_T1H_nS, LED_Latch_Delay_uS) - specifies full LED waveform timing. 114 | void begin(uint16_t, uint16_t, uint16_t, uint16_t = 300); 115 | 116 | void show(void); 117 | 118 | int busy(void); 119 | 120 | //Brightness values 0-255 121 | void setBrightness(uint8_t); 122 | 123 | //Color Balance is 3-byte number in RGB order. Each byte is a brightness value for that color. 124 | void setBalance(uint32_t); 125 | 126 | uint8_t getBrightness() { return brightness; } 127 | 128 | uint32_t getBalance() { return colorBalance; } 129 | 130 | private: 131 | static void isr(void); 132 | 133 | void genFrameBuffer(uint32_t); 134 | 135 | static uint8_t* frameBuffer; //isr() 136 | static uint32_t numbytes; //isr() 137 | static uint8_t numpins; //isr() 138 | static uint8_t pin_bitnum[NUM_DIGITAL_PINS]; //isr() 139 | static uint8_t pin_offset[NUM_DIGITAL_PINS]; //isr() 140 | DMAMEM static uint32_t bitdata[BYTES_PER_DMA * 64] __attribute__((used, aligned(32))); //isr() 141 | DMAMEM static uint32_t bitmask[4] __attribute__((used, aligned(32))); 142 | static DMAChannel dma1, dma2, dma3; 143 | static DMASetting dma2next; 144 | 145 | uint32_t update_begin_micros = 0; 146 | uint8_t brightness = 255; 147 | uint32_t colorBalance = 0xFFFFFF; 148 | uint32_t rLevel = 65025; 149 | uint32_t gLevel = 65025; 150 | uint32_t bLevel = 65025; 151 | void* drawBuffer; 152 | uint16_t stripLen; 153 | uint8_t params; 154 | uint8_t pinlist[NUM_DIGITAL_PINS]; 155 | uint16_t comp1load[3]; 156 | uint8_t serpNumber; 157 | float OC_FACTOR = 1.0; //used to reduce period of LED output 158 | uint16_t TH_TL = 1250; //nS- period of LED output 159 | uint16_t T0H = 300; //nS- duration of T0H 160 | uint16_t T1H = 750; //nS- duration of T1H 161 | uint16_t LATCH_DELAY = 300; //uS time to hold output low for LED latch. 162 | 163 | //for show context switch 164 | uint32_t bitmaskLocal[4]; 165 | uint8_t numpinsLocal; 166 | uint8_t* frameBufferLocal; 167 | uint32_t numbytesLocal; 168 | uint8_t pin_bitnumLocal[NUM_DIGITAL_PINS]; 169 | uint8_t pin_offsetLocal[NUM_DIGITAL_PINS]; 170 | }; // class ObjectFLED 171 | 172 | 173 | //fadeToColorBy(RGB_array, LED_count, Color, FadeAmount) 174 | //Fades an RGB array towards the background color by amount. 175 | void fadeToColorBy(void*, uint16_t, uint32_t, uint8_t); 176 | 177 | 178 | //drawSquare(RGB_Array, LED_Rows, LED_Cols, Y_Corner, X_Corner, square_Size) 179 | //Draws square in a 2D RGB array with lower left corner at (Y_Corner, X_Corner). 180 | //Safe to specify -Y, -X corner, safe to draw a box which partially fits on LED plane. 181 | void drawSquare(void*, uint16_t, uint16_t, int, int, uint32_t, uint32_t); 182 | 183 | #endif 184 | -------------------------------------------------------------------------------- /OjectFLED.cpp: -------------------------------------------------------------------------------- 1 | /* ObjectFLED - Teensy 4.x DMA to all pins for independent control of large and 2 | multiple LED display objects 3 | 4 | Copyright (c) 2024 Kurt Funderburg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | OctoWS2811 library code was well-studied and substantial portions of it used 17 | to implement high-speed, non-blocking DMA for LED signal output in this library. 18 | See below for a summary of changes made to the original OctoWS2811. 19 | 20 | OctoWS2811 - High Performance WS2811 LED Display Library 21 | Copyright (c) 2013 Paul Stoffregen, PJRC.COM, LLC 22 | http://www.pjrc.com/teensy/td_libs_OctoWS2811.html 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | THE SOFTWARE. 31 | */ 32 | /* 33 | Teensy 4.0 pin - port assignments 34 | GPIO6List = { 0, 1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 } //12 top, 4 bottom 35 | GPIO7List = { 6, 7, 8, 9, 10, 11, 12, 13, 32 } //8 top, 1 bottom 36 | GPIO8List = { 28, 30, 31, 34, 35, 36, 37, 38, 39 } //0 top, 9 bottom 37 | GOIO9List = { 2, 3, 4, 5, 29, 33 } //4 top, 2 bottom 38 | Teensy 4.1 pin - port assignments 39 | 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 40 | GPIO7List = { 6, 7, 8, 9, 10, 11, 12, 13, 32, 34, 35, 36, 37 } //13 top, 0 bottom 41 | GPIO8List = { 28, 30, 31, 42, 43, 44, 45, 46, 47 } //3 top, 6 bottom 42 | GOIO9List = { 2, 3, 4, 5, 29, 33, 48, 49, 50, 51, 52, 53, 54 } //6 top, 7 bottom 43 | * 4 pin groups, 4 timer groups, !4 dma channel groups: only 1 DMA group (4 ch) maps to GPIO (DMAMUX 44 | * mapping) Also, only DMA ch0-3 have periodic mode for timer trigger (p77 manual). Separate objects 45 | * cannot DMA at once. 46 | * 47 | * CHANGES: 48 | * Moved some variables so class supports multiple instances with separate LED config params 49 | * Implemented context switching so multiple instances can show() independently 50 | * Re-parameterized TH_TL, T0H, T1H, OC_FACTOR; recalc time for latch at end of show() 51 | * Added genFrameBuffer() to implement RGB order, brightness, color balance, and serpentine 52 | * Added setBrightness(), setBalance() 53 | * FrameBuffer no longer passed in, constructor now creates buffer; destructor added 54 | * Added support for per-object setting of OC factor, TH+TL, T0H, T1H, and LATCH_DELAY in begin function 55 | * Set DSE=3, SPEED=0, SRE=0 on output pins per experiment & PJRC forum guidance 56 | * New default values for TH_TL, T0H, T1H, LATCH_DELAY to work with Audio lib and more LED types 57 | * Added wait for prior xmission to complete in destructor 58 | */ 59 | 60 | #ifndef __IMXRT1062__ 61 | #error Only Teensy 4.x supported. 62 | #else 63 | #include "ObjectFLED.h" 64 | 65 | #ifndef MIN 66 | #define MIN(a,b) ((a)<(b)?(a):(b)) 67 | #endif 68 | 69 | #ifndef MAX 70 | #define MAX(a,b) ((a)>(b)?(a):(b)) 71 | #endif 72 | 73 | volatile uint32_t framebuffer_index = 0; //isr() 74 | uint8_t* ObjectFLED::frameBuffer; //isr() 75 | uint32_t ObjectFLED::numbytes; //isr() 76 | uint8_t ObjectFLED::numpins; //isr() 77 | uint8_t ObjectFLED::pin_bitnum[NUM_DIGITAL_PINS]; //isr() 78 | uint8_t ObjectFLED::pin_offset[NUM_DIGITAL_PINS]; //isr() 79 | uint32_t ObjectFLED::bitdata[BYTES_PER_DMA * 64] __attribute__((used, aligned(32))); //isr() 80 | uint32_t ObjectFLED::bitmask[4] __attribute__((used, aligned(32))); 81 | 82 | DMASetting ObjectFLED::dma2next; 83 | DMAChannel ObjectFLED::dma1; 84 | DMAChannel ObjectFLED::dma2; 85 | DMAChannel ObjectFLED::dma3; 86 | volatile bool dma_first; 87 | 88 | 89 | ObjectFLED::ObjectFLED(uint16_t numLEDs, void *drawBuf, uint8_t config, uint8_t numPins, \ 90 | const uint8_t *pinList, uint8_t serpentine) { 91 | serpNumber = serpentine; 92 | drawBuffer = drawBuf; 93 | params = config; 94 | if (numPins > NUM_DIGITAL_PINS) numPins = NUM_DIGITAL_PINS; 95 | numpins = numPins; //static/isr 96 | stripLen = numLEDs / numpins; 97 | memcpy(pinlist, pinList, numpins); 98 | if ((params & 0x3F) < 6) { 99 | frameBuffer = new uint8_t[numLEDs * 3]; //static/isr 100 | numbytes = stripLen * 3; // RGB formats //static/isr 101 | } 102 | else { 103 | frameBuffer = new uint8_t[numLEDs * 4]; //static/isr 104 | numbytes = stripLen * 4; // RGBW formats //static/isr 105 | } 106 | 107 | numpinsLocal = numPins; 108 | frameBufferLocal = frameBuffer; 109 | numbytesLocal = numbytes; 110 | } // ObjectFLED constructor 111 | 112 | 113 | extern "C" void xbar_connect(unsigned int input, unsigned int output); // in pwm.c 114 | static volatile uint32_t *standard_gpio_addr(volatile uint32_t *fastgpio) { 115 | return (volatile uint32_t *)((uint32_t)fastgpio - 0x01E48000); 116 | } 117 | 118 | 119 | void ObjectFLED::begin(uint16_t latchDelay) { 120 | LATCH_DELAY = latchDelay; 121 | begin(); 122 | } 123 | 124 | 125 | void ObjectFLED::begin(double OCF, uint16_t latchDelay) { 126 | OC_FACTOR = (float)OCF; 127 | LATCH_DELAY = latchDelay; 128 | begin(); 129 | } 130 | 131 | 132 | void ObjectFLED::begin(uint16_t period, uint16_t t0h, uint16_t t1h, uint16_t latchDelay) { 133 | TH_TL = period; 134 | T0H = t0h; 135 | T1H = t1h; 136 | LATCH_DELAY = latchDelay; 137 | begin(); 138 | } 139 | 140 | 141 | // INPUT stripLen, frameBuffer, params, numPins, pinList 142 | // GPIOR bits set for pins[i] -> bitmask, pin_bitnum[i], pin_offset[i] 143 | // init timers, xbar to DMA, DMA bitdata -> GPIOR; clears frameBuffer (total LEDs * 3 bytes) 144 | void ObjectFLED::begin(void) { 145 | numpins = numpinsLocal; //needed to compute pin mask/offset & bitmask since static for isr 146 | // Set each pin's bitmask bit, store offset & bit# for pin 147 | memset(bitmask, 0, sizeof(bitmask)); 148 | for (uint32_t i=0; i < numpins; i++) { 149 | uint8_t pin = pinlist[i]; 150 | if (pin >= NUM_DIGITAL_PINS) continue; // ignore illegal pins 151 | uint8_t bit = digitalPinToBit(pin); // pin's bit index in word port DR 152 | // which GPIO R controls this pin: 0-3 map to GPIO6-9 then map to DMA compat GPIO1-4 153 | uint8_t offset = ((uint32_t)portOutputRegister(pin) - (uint32_t)&GPIO6_DR) >> 14; 154 | if (offset > 3) continue; //ignore unknown pins 155 | pin_bitnum[i] = bit; //static/isr 156 | pin_offset[i] = offset; //static/isr 157 | uint32_t mask = 1 << bit; //mask32 = bit set @position in GPIO DR 158 | bitmask[offset] |= mask; //bitmask32[0..3] = collective pin bit masks for each GPIO DR 159 | //bit7:6 SPEED; bit 5:3 DSE; bit0 SRE (default SPEED = 0b10; def. DSE = 0b110) 160 | *portControlRegister(pin) &= ~0xF9; //clear SPEED, DSE, SRE 161 | *portControlRegister(pin) |= ((OUTPUT_PAD_SPEED & 0x3) << 6) | \ 162 | ((OUTPUT_PAD_DSE & 0x7) << 3); //DSE = 0b011 for LED overclock 163 | //clear pin bit in IOMUX_GPR26 to map GPIO6-9 to GPIO1-4 for DMA 164 | *(&IOMUXC_GPR_GPR26 + offset) &= ~mask; 165 | *standard_gpio_addr(portModeRegister(pin)) |= mask; //GDIR? bit flag set output mode 166 | } 167 | //stash context for multi-show 168 | memcpy(bitmaskLocal, bitmask, 16); 169 | memcpy(pin_bitnumLocal, pin_bitnum, numpins); 170 | memcpy(pin_offsetLocal, pin_offset, numpins); 171 | 172 | arm_dcache_flush_delete(bitmask, sizeof(bitmask)); //can't DMA from cached memory 173 | 174 | // Set up 3 timers to create waveform timing events 175 | comp1load[0] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)TH_TL / OC_FACTOR ); 176 | comp1load[1] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T0H / OC_FACTOR ); 177 | comp1load[2] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T1H / (1.0 + ((OC_FACTOR - 1.0)/3)) ); 178 | TMR4_ENBL &= ~7; 179 | TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR; 180 | TMR4_CSCTRL0 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN; 181 | TMR4_CNTR0 = 0; 182 | TMR4_LOAD0 = 0; 183 | TMR4_COMP10 = comp1load[0]; 184 | TMR4_CMPLD10 = comp1load[0]; 185 | TMR4_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(3); 186 | TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE; 187 | TMR4_CNTR1 = 0; 188 | TMR4_LOAD1 = 0; 189 | TMR4_COMP11 = comp1load[1]; // T0H 190 | TMR4_CMPLD11 = comp1load[1]; 191 | TMR4_CTRL1 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3); 192 | TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE; 193 | TMR4_CNTR2 = 0; 194 | TMR4_LOAD2 = 0; 195 | TMR4_COMP12 = comp1load[2]; // T1H 196 | TMR4_CMPLD12 = comp1load[2]; 197 | TMR4_CTRL2 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3); 198 | 199 | // route the timer outputs through XBAR to edge trigger DMA request: only 4 mappings avail. 200 | CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON); 201 | xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_DMA_CH_MUX_REQ30); 202 | xbar_connect(XBARA1_IN_QTIMER4_TIMER1, XBARA1_OUT_DMA_CH_MUX_REQ31); 203 | xbar_connect(XBARA1_IN_QTIMER4_TIMER2, XBARA1_OUT_DMA_CH_MUX_REQ94); 204 | XBARA1_CTRL0 = XBARA_CTRL_STS1 | XBARA_CTRL_EDGE1(3) | XBARA_CTRL_DEN1 | 205 | XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0; 206 | XBARA1_CTRL1 = XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0; 207 | 208 | // configure DMA channels 209 | dma1.begin(); 210 | dma1.TCD->SADDR = bitmask; // source 4*32b GPIO pin mask 211 | dma1.TCD->SOFF = 8; // bytes offset added to SADDR after each transfer 212 | // SMOD(4) low bits of SADDR to update with adds of SOFF 213 | // SSIZE(3) code for 64 bit transfer size DSIZE(2) code for 32 bit transfer size 214 | dma1.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2); 215 | dma1.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | // Dest minor loop offsetting enable 216 | DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) | 217 | DMA_TCD_NBYTES_MLOFFYES_NBYTES(16); // #bytes to tansfer, offsetting enabled 218 | dma1.TCD->SLAST = 0; // add to SADDR after xfer 219 | dma1.TCD->DADDR = &GPIO1_DR_SET; 220 | dma1.TCD->DOFF = 16384; //&GPIO1_DR_SET + DOFF = next &GPIO2_DR_SET 221 | dma1.TCD->CITER_ELINKNO = numbytes * 8; // CITER outer loop count (linking disabled) = # LED bits to write 222 | dma1.TCD->DLASTSGA = -65536; // add to DADDR after xfer 223 | dma1.TCD->BITER_ELINKNO = numbytes * 8; // Beginning CITER (not decremented by transfer) 224 | dma1.TCD->CSR = DMA_TCD_CSR_DREQ; // channel ERQ field cleared when minor loop completed 225 | dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_0); // only 4 XBAR1 triggers (DMA MUX mapping) 226 | 227 | dma2next.TCD->SADDR = bitdata; //uint32_t bitdata[BYTES_PER_DMA*64] 228 | dma2next.TCD->SOFF = 8; 229 | dma2next.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_DSIZE(2); 230 | dma2next.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | 231 | DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) | 232 | DMA_TCD_NBYTES_MLOFFYES_NBYTES(16); 233 | dma2next.TCD->SLAST = 0; 234 | dma2next.TCD->DADDR = &GPIO1_DR_CLEAR; 235 | dma2next.TCD->DOFF = 16384; 236 | dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8; 237 | dma2next.TCD->DLASTSGA = (int32_t)(dma2next.TCD); 238 | dma2next.TCD->BITER_ELINKNO = BYTES_PER_DMA * 8; 239 | dma2next.TCD->CSR = 0; 240 | 241 | dma2.begin(); 242 | dma2 = dma2next; // copies TCD 243 | dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_1); 244 | dma2.attachInterrupt(isr); 245 | 246 | dma3.begin(); 247 | dma3.TCD->SADDR = bitmask; 248 | dma3.TCD->SOFF = 8; 249 | dma3.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2); 250 | dma3.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | 251 | DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) | 252 | DMA_TCD_NBYTES_MLOFFYES_NBYTES(16); 253 | dma3.TCD->SLAST = 0; 254 | dma3.TCD->DADDR = &GPIO1_DR_CLEAR; 255 | dma3.TCD->DOFF = 16384; 256 | dma3.TCD->CITER_ELINKNO = numbytes * 8; 257 | dma3.TCD->DLASTSGA = -65536; 258 | dma3.TCD->BITER_ELINKNO = numbytes * 8; 259 | dma3.TCD->CSR = DMA_TCD_CSR_DREQ | DMA_TCD_CSR_DONE; 260 | dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_2); 261 | } // begin() 262 | 263 | 264 | //*dest = *bitdata + pin offset 265 | //*pixels = pin's block in frameBuffer 266 | //mask = pin's bit position in GPIOR 267 | //set a pin's mask32 for each color bit=0 at every 4*words32 in bitdata+offset 268 | void fillbits(uint32_t *dest, const uint8_t *pixels, int n, uint32_t mask) { 269 | do { 270 | uint8_t pix = *pixels++; 271 | if (!(pix & 0x80)) *dest |= mask; 272 | dest += 4; 273 | if (!(pix & 0x40)) *dest |= mask; 274 | dest += 4; 275 | if (!(pix & 0x20)) *dest |= mask; 276 | dest += 4; 277 | if (!(pix & 0x10)) *dest |= mask; 278 | dest += 4; 279 | if (!(pix & 0x08)) *dest |= mask; 280 | dest += 4; 281 | if (!(pix & 0x04)) *dest |= mask; 282 | dest += 4; 283 | if (!(pix & 0x02)) *dest |= mask; 284 | dest += 4; 285 | if (!(pix & 0x01)) *dest |= mask; 286 | dest += 4; 287 | } while (--n > 0); 288 | } 289 | 290 | 291 | void ObjectFLED::genFrameBuffer(uint32_t serp) { 292 | uint32_t j = 0; 293 | int jChange = -3; 294 | if (serp == 0) { // use faster loops if no serp 295 | switch (params & 0x3F) { 296 | case CORDER_RGBW: // R,G,B = R,G,B - MIN(R,G,B); W = MIN(R,G,B) 297 | for (uint16_t i = 0; i < (numbytes * numpins); i += 4) { 298 | uint8_t minRGB = MIN(*((uint8_t*)drawBuffer + j) * rLevel / 65025, \ 299 | *((uint8_t*)drawBuffer + j + 1) * rLevel / 65025); 300 | minRGB = MIN(minRGB, *((uint8_t*)drawBuffer + j + 2) * rLevel / 65025); 301 | *(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025 - minRGB; 302 | *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025 - minRGB; 303 | *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025 - minRGB; 304 | *(frameBuffer + i + 3) = minRGB; 305 | j += 3; 306 | } //for(leds in drawbuffer) 307 | break; 308 | case CORDER_GBR: 309 | for (uint16_t i = 0; i < (numbytes * numpins); i += 3) { 310 | *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025; 311 | *(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025; 312 | *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025; 313 | j += 3; 314 | } //for(leds in drawbuffer) 315 | break; 316 | case CORDER_BGR: 317 | for (uint16_t i = 0; i < (numbytes * numpins); i += 3) { 318 | *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025; 319 | *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025; 320 | *(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025; 321 | j += 3; 322 | } //for(leds in drawbuffer) 323 | break; 324 | case CORDER_BRG: 325 | for (uint16_t i = 0; i < (numbytes * numpins); i += 3) { 326 | *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025; 327 | *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025; 328 | *(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025; 329 | j += 3; 330 | } //for(leds in drawbuffer) 331 | break; 332 | case CORDER_GRB: 333 | for (uint16_t i = 0; i < (numbytes * numpins); i += 3) { 334 | *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025; 335 | *(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025; 336 | *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025; 337 | j += 3; 338 | } //for(leds in drawbuffer) 339 | break; 340 | case CORDER_RGB: 341 | default: 342 | for (uint16_t i = 0; i < (numbytes * numpins); i += 3) { 343 | *(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025; 344 | *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025; 345 | *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025; 346 | j += 3; 347 | } //for(leds in drawbuffer) 348 | } // switch() 349 | } else { //serpentine 350 | switch (params & 0x3F) { 351 | case CORDER_RGBW: // R,G,B = R,G,B - MIN(R,G,B); W = MIN(R,G,B) 352 | for (uint16_t i = 0; i < (numbytes * numpins); i += 4) { 353 | uint8_t minRGB = MIN(*((uint8_t*)drawBuffer + j) * rLevel / 65025, \ 354 | * ((uint8_t*)drawBuffer + j + 1) * rLevel / 65025); 355 | minRGB = MIN(minRGB, *((uint8_t*)drawBuffer + j + 2) * rLevel / 65025); 356 | if (i % (serp * 4) == 0) { 357 | if (jChange < 0) { j = i / 4 * 3; jChange = 3; } 358 | else { j = (i / 4 + serp - 1) * 3; jChange = -3; } 359 | } 360 | *(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025 - minRGB; 361 | *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025 - minRGB; 362 | *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025 - minRGB; 363 | *(frameBuffer + i + 3) = minRGB; 364 | j += jChange; 365 | } //for(leds in drawbuffer) 366 | break; 367 | case CORDER_GBR: 368 | for (uint16_t i = 0; i < (numbytes * numpins); i += 3) { 369 | if (i % (serp * 3) == 0) { 370 | if (jChange < 0) { j = i; jChange = 3; } 371 | else { j = i + (serp - 1) * 3; jChange = -3; } 372 | } 373 | *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025; 374 | *(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025; 375 | *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025; 376 | j += 3; 377 | } //for(leds in drawbuffer) 378 | break; 379 | case CORDER_BGR: 380 | for (uint16_t i = 0; i < (numbytes * numpins); i += 3) { 381 | if (i % (serp * 3) == 0) { 382 | if (jChange < 0) { j = i; jChange = 3; } 383 | else { j = i + (serp - 1) * 3; jChange = -3; } 384 | } 385 | *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025; 386 | *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025; 387 | *(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025; 388 | j += 3; 389 | } //for(leds in drawbuffer) 390 | break; 391 | case CORDER_BRG: 392 | for (uint16_t i = 0; i < (numbytes * numpins); i += 3) { 393 | if (i % (serp * 3) == 0) { 394 | if (jChange < 0) { j = i; jChange = 3; } 395 | else { j = i + (serp - 1) * 3; jChange = -3; } 396 | } 397 | *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025; 398 | *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025; 399 | *(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025; 400 | j += jChange; 401 | } //for(leds in drawbuffer) 402 | break; 403 | case CORDER_GRB: 404 | for (uint16_t i = 0; i < (numbytes * numpins); i += 3) { 405 | if (i % (serp * 3) == 0) { 406 | if (jChange < 0) { j = i; jChange = 3; } 407 | else { j = i + (serp - 1) * 3; jChange = -3; } 408 | } 409 | *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025; 410 | *(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025; 411 | *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025; 412 | j += jChange; 413 | } //for(leds in drawbuffer) 414 | break; 415 | case CORDER_RGB: 416 | default: 417 | for (uint16_t i = 0; i < (numbytes * numpins); i += 3) { 418 | if (i % (serp * 3) == 0) { 419 | if (jChange < 0) { j = i; jChange = 3; } 420 | else { j = i + (serp - 1) * 3; jChange = -3; } 421 | } 422 | *(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025; 423 | *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025; 424 | *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025; 425 | j += jChange; 426 | } //for(leds in drawbuffer) 427 | } // switch() 428 | } // else serpentine 429 | } //genFrameBuffer() 430 | 431 | 432 | // pre-show prior transfer wait, copies drawBuffer -> frameBuffer 433 | // resets timers, clears pending DMA reqs 434 | // fills bitdata[BYTES_PER_DMA * 64 * 4 bytes] from frameBuffer with 4-block bitmasks for 0's in led data 435 | // 4 word32s for each bit in (led data)/pin = 16 * 8 = 96 bitdata bytes for each LED byte: 288 bytes / LED 436 | // launches DMA with IRQ activation to reload bitdata from frameBuffer 437 | void ObjectFLED::show(void) { 438 | while (!dma3.complete()); // wait for dma to complete before reset/re-use 439 | 440 | //Restore context if needed 441 | if (frameBuffer != frameBufferLocal) { 442 | numpins = numpinsLocal; 443 | frameBuffer = frameBufferLocal; 444 | numbytes = numbytesLocal; 445 | memcpy(bitmask, bitmaskLocal, 16); 446 | memcpy(pin_bitnum, pin_bitnumLocal, numpins); 447 | memcpy(pin_offset, pin_offsetLocal, numpins); 448 | arm_dcache_flush_delete(bitmask, sizeof(bitmask)); //can't DMA from cached memory 449 | // Restore 3 timers to create waveform timing events 450 | TMR4_COMP10 = comp1load[0]; 451 | TMR4_CMPLD10 = comp1load[0]; 452 | TMR4_COMP11 = comp1load[1]; // T0H 453 | TMR4_CMPLD11 = comp1load[1]; 454 | TMR4_COMP12 = comp1load[2]; // T1H 455 | TMR4_CMPLD12 = comp1load[2]; 456 | //restore DMA loop control 457 | dma1.TCD->CITER_ELINKNO = numbytes * 8; // CITER outer loop count (linking disabled) = # LED bits to write 458 | dma1.TCD->BITER_ELINKNO = numbytes * 8; // Beginning CITER (not decremented by transfer) 459 | dma3.TCD->CITER_ELINKNO = numbytes * 8; 460 | dma3.TCD->BITER_ELINKNO = numbytes * 8; 461 | } //done restoring context 462 | 463 | genFrameBuffer(serpNumber); 464 | 465 | // disable timers 466 | uint16_t enable = TMR4_ENBL; 467 | TMR4_ENBL = enable & ~7; 468 | 469 | // force all timer outputs to logic low 470 | TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR; 471 | TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE; 472 | TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE; 473 | 474 | // clear any prior pending DMA requests 475 | XBARA1_CTRL0 |= XBARA_CTRL_STS1 | XBARA_CTRL_STS0; 476 | XBARA1_CTRL1 |= XBARA_CTRL_STS0; 477 | 478 | // fill the DMA transmit buffer 479 | memset(bitdata, 0, sizeof(bitdata)); //BYTES_PER_DMA * 64 words32 480 | uint32_t count = numbytes; //bytes per strip 481 | if (count > BYTES_PER_DMA*2) count = BYTES_PER_DMA*2; 482 | framebuffer_index = count; //ptr to framebuffer last byte output 483 | 484 | //Sets each pin mask in bitdata32[BYTES_PER_DMA*64] for every 0 bit of pin's frameBuffer block bytes 485 | for (uint32_t i=0; i < numpins; i++) { //for each pin 486 | fillbits(bitdata + pin_offset[i], (uint8_t *)frameBuffer + i*numbytes, 487 | count, 1<SADDR = bitdata; 494 | dma2.TCD->DADDR = &GPIO1_DR_CLEAR; 495 | dma2.TCD->CITER_ELINKNO = count * 8; 496 | dma2.TCD->CSR = DMA_TCD_CSR_DREQ; 497 | } else { 498 | dma2.TCD->SADDR = bitdata; 499 | dma2.TCD->DADDR = &GPIO1_DR_CLEAR; 500 | dma2.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8; 501 | dma2.TCD->CSR = 0; 502 | dma2.TCD->CSR = DMA_TCD_CSR_INTMAJOR | DMA_TCD_CSR_ESG; 503 | dma2next.TCD->SADDR = bitdata + BYTES_PER_DMA*32; 504 | dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8; 505 | if (numbytes <= BYTES_PER_DMA*3) { 506 | dma2next.TCD->CSR = DMA_TCD_CSR_ESG; 507 | } else { 508 | dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR; 509 | } 510 | dma_first = true; 511 | } 512 | dma3.clearComplete(); 513 | dma1.enable(); 514 | dma2.enable(); 515 | dma3.enable(); 516 | 517 | // initialize timers 518 | TMR4_CNTR0 = 0; 519 | TMR4_CNTR1 = comp1load[0] + 1; 520 | TMR4_CNTR2 = comp1load[0] + 1; 521 | 522 | // wait for last LED reset to finish 523 | while (micros() - update_begin_micros < numbytes * 8 * TH_TL / OC_FACTOR / 1000 + LATCH_DELAY); 524 | 525 | // start everything running! 526 | TMR4_ENBL = enable | 7; 527 | update_begin_micros = micros(); 528 | } // show() 529 | 530 | 531 | //INPUT: dma2, dma2next, bitdata, framebuffer_inedex, numpins, numbytes, pin_offset[], pin_bitnum[] 532 | //Reads next block of framebuffer -> fillbits() -> bitdata 533 | //Checks for last block to transfer, next to last, or not to update dma2next major loop 534 | void ObjectFLED::isr(void) 535 | { 536 | // first ack the interrupt 537 | dma2.clearInterrupt(); 538 | 539 | // fill (up to) half the transmit buffer with new fillbits(frameBuffer data) 540 | //digitalWriteFast(12, HIGH); 541 | uint32_t *dest; 542 | if (dma_first) { 543 | dma_first = false; 544 | dest = bitdata; 545 | } else { 546 | dma_first = true; 547 | dest = bitdata + BYTES_PER_DMA*32; 548 | } 549 | memset(dest, 0, sizeof(bitdata)/2); 550 | uint32_t index = framebuffer_index; 551 | uint32_t count = numbytes - framebuffer_index; 552 | if (count > BYTES_PER_DMA) count = BYTES_PER_DMA; 553 | framebuffer_index = index + count; 554 | for (int i=0; i < numpins; i++) { 555 | fillbits(dest + pin_offset[i], (uint8_t *)frameBuffer + index + i*numbytes, 556 | count, 1<SADDR = dest; 563 | dma2next.TCD->CITER_ELINKNO = count * 8; 564 | uint32_t remain = numbytes - (index + count); 565 | if (remain == 0) { 566 | dma2next.TCD->CSR = DMA_TCD_CSR_DREQ; 567 | } else if (remain <= BYTES_PER_DMA) { 568 | dma2next.TCD->CSR = DMA_TCD_CSR_ESG; 569 | } else { 570 | dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR; 571 | } 572 | } // isr() 573 | 574 | 575 | int ObjectFLED::busy(void) 576 | { 577 | if (micros() - update_begin_micros < numbytes * TH_TL / OC_FACTOR / 1000 * 8 + LATCH_DELAY) { 578 | return 1; 579 | } 580 | return 0; 581 | } 582 | 583 | 584 | void ObjectFLED::setBrightness(uint8_t brightLevel) { 585 | brightness = brightLevel; 586 | rLevel = brightness * (colorBalance >> 16); 587 | gLevel = brightness * ((colorBalance >> 8) & 0xFF); 588 | bLevel = brightness * (colorBalance & 0xFF); 589 | } 590 | 591 | 592 | void ObjectFLED::setBalance(uint32_t balMask) { 593 | colorBalance = balMask & 0xFFFFFF; 594 | rLevel = brightness * (colorBalance >> 16); 595 | gLevel = brightness * ((colorBalance >> 8) & 0xFF); 596 | bLevel = brightness * (colorBalance & 0xFF); 597 | } 598 | 599 | 600 | //Fades CRGB array towards the background color by amount. 601 | void fadeToColorBy(void* leds, uint16_t count, uint32_t color, uint8_t fadeAmt) { 602 | for (uint32_t x = 0; x < count * 3; x += 3) { 603 | //fade red 604 | *((uint8_t*)leds + x) = (( *((uint8_t*)leds + x) * (1 + (255 - fadeAmt))) >> 8) + \ 605 | (( ((color >> 16) & 0xFF) * (1 + fadeAmt)) >> 8); 606 | //fade green 607 | *((uint8_t*)leds + x + 1) = (( *((uint8_t*)leds + x + 1) * (1 + (255 - fadeAmt))) >> 8) + \ 608 | (( ((color >> 8) & 0xFF) * (1 + fadeAmt)) >> 8); 609 | //fade blue 610 | *((uint8_t*)leds + x + 2) = (( *((uint8_t*)leds + x + 2) * (1 + (255 - fadeAmt))) >> 8) + \ 611 | (( (color & 0xFF) * (1 + fadeAmt)) >> 8); 612 | } 613 | } //fadeToColorBy() 614 | 615 | 616 | // Safely draws box even if partially offscreen on 2D CRGB array 617 | void drawSquare(void* leds, uint16_t planeY, uint16_t planeX, int yCorner, int xCorner, uint32_t size, uint32_t color) { 618 | if (size != 0) { size--; } 619 | else { return; } 620 | for (int x = xCorner; x <= xCorner + (int)size; x++) { 621 | // if validX { if validY+S {draw Y+S,X}; if validY {draw Y, X} } 622 | if ((x >= 0) && (x < planeX)) { //valid X 623 | if ((yCorner >= 0) && (yCorner < planeY)) { 624 | *((uint8_t*)leds + (yCorner * planeX + x) * 3) = ((color >> 16) & 0xFF); 625 | *((uint8_t*)leds + (yCorner * planeX + x) * 3 + 1) = ((color >> 8) & 0xFF); 626 | *((uint8_t*)leds + (yCorner * planeX + x) * 3 + 2) = (color & 0xFF); 627 | } 628 | if ((yCorner + size >= 0) && (yCorner + size < planeY)) { 629 | *((uint8_t*)leds + ((yCorner + size) * planeX + x) * 3) = ((color >> 16) & 0xFF); 630 | *((uint8_t*)leds + ((yCorner + size) * planeX + x) * 3 + 1) = ((color >> 8) & 0xFF); 631 | *((uint8_t*)leds + ((yCorner + size) * planeX + x) * 3 + 2) = (color & 0xFF); 632 | } 633 | } //if valid x 634 | } //for x 635 | for (int y = yCorner; y <= yCorner + (int)size; y++) { 636 | if ((y >= 0) && (y < planeY)) { //valid y 637 | if ((xCorner >= 0) && (xCorner < planeX)) { 638 | *((uint8_t*)leds + (xCorner + y * planeX) * 3) = ((color >> 16) & 0xFF); 639 | *((uint8_t*)leds + (xCorner + y * planeX) * 3 + 1) = ((color >> 8) & 0xFF); 640 | *((uint8_t*)leds + (xCorner + y * planeX) * 3 + 2) = (color & 0xFF); 641 | } 642 | if ((xCorner + size >= 0) && (xCorner + size < planeX)) { 643 | *((uint8_t*)leds + (xCorner + size + y * planeX) * 3) = ((color >> 16) & 0xFF); 644 | *((uint8_t*)leds + (xCorner + size + y * planeX) * 3 + 1) = ((color >> 8) & 0xFF); 645 | *((uint8_t*)leds + (xCorner + size + y * planeX) * 3 + 2) = (color & 0xFF); 646 | } 647 | } //if valid y 648 | } //for y 649 | } // drawSquare() 650 | 651 | #endif // __IMXRT1062__ 652 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ObjectFLED - Non-Blocking Teensy 4.x DMA Display Driver for Clockless WS28xx LEDs 2 | 3 | FastLED-friendly LED display driver allows for configuration and control of multiple LED display 4 | devices independently in code. Display objects use DMA transfer (from [OctoWS2811 library](http://www.pjrc.com/teensy/td_libs_OctoWS2811.html)) 5 | on any or all 40 or 55 digital pins of Teensy 4.0 or 4.1 to drive digital LEDs with massive 6 | parallelism if needed. 7 | 8 | The show() function is non-blocking, returning control to the graphics drawing code in just 6% of 9 | the time it takes to complete buffer transmission to an LED string (tested with WS2812B, 1.6 10 | overclock factor, 256 LED per pin x 16 pins = 4096 LEDs total at 204 back-to-back fps). 11 | 12 | ObjectFLED works with FastLED arrays of CRGB, or other drawing buffer in RGB 3-byte format. 13 | 14 | ### Glossary: 15 | 16 | * "LED object" or "display object" refers to the ObjectFLED display object(s) in your code. 17 | * "LED device" refers to physical LEDs: strings, planes, cubes, etc. 18 | * "Segment" refers to physical LEDs connected to a single pin. This can be a string or a subset 19 | of LED devices in a larger LED device (rows of a plane, planes of a cube, spokes of a wheel, etc.) 20 | 21 | ## SUPPORTED 22 | 23 | - For Teensy 4.x only. It may be possible to add other boards if they support DMA driven by 24 | configurable timers, and ability to map digital pins to a data register which DMA can target. 25 | - LED data color formats: RGB, GRB, BRG, GBR, BGR and RGBW 26 | - Known compatible LED types: SK6812, WS2811, WS2812, WS2812B, WS2812C, WS2812D, WS2812S, WS2813, 27 | WS2813E, WS2815B 28 | 29 | 30 | ## MAIN BENEFITS 31 | 32 | * Independently configure, control and display multiple LED devices connected to your Teensy 4.x, 33 | even if the devices use different LED types with different specs. Combine LED devices into a 34 | single display object, or define separate objects for segments of each device, or just plain one 35 | display object for each device. It is also possible to define 2 display objects to display the 36 | same drawing buffer on 2 different LED devices. 37 | 38 | * Large LED devices can be driven with parallel output to separate segments of the device, allowing 39 | for multi-fold increase in refresh rate. ObjectFLED breaks your drawing buffer into segments and 40 | writes them to the pins simultaneously in the specified order. Simply define an ordered pin list 41 | for your object, in the same order they are connected physically to the segments in your LED device. 42 | 43 | * ObjectFLED works with arrays of CRGB for full compatibility with the rich suite of LED graphics 44 | functions of FastLED. But you can use any memory object for drawing buffer, so long as it is 3- 45 | bytes per LED in RGB order. 46 | 47 | * You can tweak the shape of the LED data waveform generated by this driver to achieve the highest 48 | possible overclock. ObjectFLED sends a latch pulse at the end of every frame, and defaults to 49 | 300 uS latch period. Some WS2812s specify only 50 uS latch period. To make a noticable increase 50 | in refresh rate, try 75 uS latch if yours are rated for 50 (I had to use 72 on a 50 uS latch LED). 51 | 52 | * Show() function has built-in handling for serpentine rows of a plane, color order, brightness, 53 | and color balance for each object. Like in FastLED, these are applied to the frame buffer, not 54 | your drawing buffer. Get and set functions for brightness and colormask are included (the ones in 55 | FastLED won't work on ObjectFLED display objects). These are applied independently to each display 56 | object in your sketch. 57 | 58 | * Accessory functions included to fade a drawing array to a color other than black, and to draw a 59 | square on an LED plane. 60 | 61 | 62 | ## USING OBJECTFLED WITH FASTLED 63 | 64 | As of FastLED relase 3.9.8, FastLED has built-in non-blocking DMA driver for Teensy 4.x, ported 65 | from ObjectFLED. Since this release, it is possible to have the speed and parallelism benefits 66 | of ObjectFLED without installing and including ObjectFLED library. However there are some 67 | functional differences between running FastLED with and without including ObjectFLED.h, which may 68 | impact your project: 69 | 70 | |FastLED Without ObjectFLED.h|FastLED With ObjectFLED.h|| 71 | |:----------:|:----------:|:--------------------------------------------------------------------| 72 | |YES|YES|Explicitly assign pins to 1D arrays for LED strings| 73 | |NO|YES|Auto-assign ordered pin list to segments of 2D and 3D arrays for LED string collections, planes and cubes| 74 | |YES|NO|Show, brightness, and color balance are global to all LED objects defined in sketch| 75 | |NO|YES|Show, brightness, and color balance are specific to each LED object defined in sketch| 76 | |NO|YES|Built-in support for serpentine rows in LED planes| 77 | |NO|YES|Full control of LED signal waveform timing for overclocking| 78 | 79 | 2 new sketches were added to the examples directory to illustrate both methods: UsesFastLED.ino and 80 | UsesObjectFLED.ino. 81 | 82 | 83 | ## LED DEVICES 84 | 85 | Physical LED devices are connected to Teensy's digital pins via a buffer circuit to step the 3.3v 86 | Teensy squarewave up to 5v for the LEDs. For buffering, use [OctoWS2811 boards](https://www.pjrc.com/store/octo28_adaptor.html) or SN74AHCT125 or equivalent. 87 | 88 | LED planes can have their data paths physically wired for serpentine (reverse direction on every 89 | other row). ObjectFLED will automatically perform serpentine output if you specify an LED count 90 | (row size) for your display object. However, with planes in a cube, ObjectFLED expects the last 91 | LED of a plane to connect to the first LED of the next plane (no plane-level serpentine, only row- 92 | level). 93 | 94 | When taking advantage of parallel inputs into an LED device, each pin is assumed to drive the same 95 | number of LEDs (or rows or planes). When you define your ObjectFLED object, provide an ordered pin 96 | list which matches the order in which the pins connect to the device. 97 | 98 | Individual LEDs come with various formats for RGB color order, data signal clock frequency, and 99 | pulse timing specs. ObjectFLED defaults will work for popular RGB-order LEDs with 800KHz clock and 100 | 300uS latch delay. Use the begin(params) function on an ObjectFLED object in order to use timing 101 | specs from your LED's datasheet. Specify the color order when you instantiate ObjectFLED. 102 | 103 | 104 | ## USAGE 105 | 106 | ### ObjectFLED CLASS 107 | 108 | __ObjectFLED(uint16_t numLEDs, void* drawBuf, uint8_t config, uint8_t numPins, const uint8_t* pinList, 109 | uint8_t serpentine = 0);__ 110 | 111 | - numLEDs = total number of LEDs in the device(s) represented by your drawing buffer 112 | - drawBuf = drawing array of CRGB or similar format which contains your graphics NOTE: multi-dimensional 113 | drawing arrays must be in row-major order. That means myCube[ Z ][ Y ][ X ], not myCube[ X ][ Y ][ Z ], 114 | where Z is plane, Y is row, X is pixel in row. 115 | - config = predefined value for color order (ie- CORDER_RGB, see above for supported list) 116 | - numPins = number of pins connected to the LED device(s) 117 | - pinList = array of pin numbers in the order which they are connected to successive segments 118 | - serpentine (optional) = number of LEDs in each row for serpentine 119 | 120 | **Example:** 121 | 122 | CRGB myCube[16][16][16]; 123 | uint8_t pinList[8] = {1, 8, 14, 17, 24, 3, 4, 5}; //8 pins for 16 planes: 2 planes per pin 124 | //Pins are attached to each successive pair of planes in the given list order 125 | ObjectFLED dispCube( 16*16*16, myCube, CORDER_GRB, 8, pinList, 16 ); //serpentine 16 LEDs/row 126 | 127 | **Best Practice:** 128 | Use variable names which partially match for creating a drawing array and it's matching display 129 | object. 130 | 131 | CRGB grid4[Y][X]; 132 | ObjectFLED dspGrid4( Y*X, grid4, etc. ); 133 | 134 | 135 | ### begin() FUNCTION 136 | 137 | Called once in setup for the display object 138 | 139 | // Default timing 800KHz clock, 300 nS T0H, 750 nS T1H, and 300 uS latch delay 140 | __begin(void);__ 141 | 142 | // Override the default latch delay 143 | __begin(uint16_t latchDelay);__ 144 | 145 | // Overclock default timing and optionally overrde default latch delay 146 | __begin(double OCF, uint16_t latchDelay = 300);__ 147 | 148 | // Fully specify output waveform timing TH_TL (clk period), T0H, T1H, and optionally Latch Delay 149 | // Overclocking is achieved by shortening period rather than using OCF 150 | __begin(uint16_t period, uint16_t t0h, uint16_t t1h, uint16_t latchDelay = 300);__ 151 | 152 | - OCF = Overclocking factor multiples the clock rate (by dividing the pulse width values below) 153 | - period = nS duration of a full LED data pulse (from LED datasheet) NOTE: For 800KHz clock 154 | (default), period = 1250nS. 155 | - t0h = nS duration of high portion of pulse for LED data 0 (from LED datasheet) 156 | - t1h = nS duration of high portion of pulse for LED data 1 (from LED datasheet) 157 | - latchDelay = uS time to hold data low for LEDs to latch color data to DACs (from LED datasheet) 158 | 159 | **Example:** 160 | 161 | dispCube.begin(1.5, 72); //overclocks by factor 1.5 (1200KHz) and sets 72uS latch delay 162 | dispVid.begin(1250, 420, 840, 72); //420nS, 840nS are longer than typical 163 | 164 | 165 | ### show() FUNCTION 166 | 167 | See MAIN BENEFITS above for details. When calling show() back-to-back, each must wait for the 168 | prior to complete it's latch pulse at the end of writing. This is also true when calling show() 169 | for one object right after calling show() for another object. All display objects share the same 170 | DMA-Timer pipeline in hardware. Therefore, for code which controls a large LED device and a small 171 | one, with sequential calls to each show(): show the small one first, then show the large one, then 172 | update your drawing while the large display is still DMA-transmitting 94% of it's LED data. 173 | 174 | **Example:** 175 | 176 | dispCube.show(); //dispCube has far fewer LEDs than dispVid (it is a small cube) 177 | dispVid.show(); //by calling show on the large object last, code can re-draw while 178 | //~94% of dispVid's LED data is being written by DMA under the covers. 179 | //if you call dispCube.show() last, it would wait for all that 94% of 180 | //dispVid data to finish, then wait for latch cycle, before it ran. 181 | 182 | 183 | ### setBrightness(), getBrightness() FUNCTIONS 184 | 185 | Brightness values 0-255. Brightness is applied by show() to frame buffer, not your 186 | drawing buffer. 187 | 188 | __setBrightness(uint8_t);__ 189 | 190 | __uint8_t getBrightness();__ 191 | 192 | 193 | ### setBalance(), getBalance() FUNCTIONS 194 | 195 | Color Balance is 3-byte number in RGB order. Each byte is a brightness value for that color. 196 | Like brightness, this is applied by show() to frame buffer, not to your drawing buffer. 197 | 198 | __setBalance(uint32_t);__ 199 | 200 | __uint32_t getBalance();__ 201 | 202 | 203 | ## ACCESSORY FUNCTIONS 204 | 205 | These are not part of display objects, call them without object specifier. Unlike brightness and 206 | balance, these functions operate on your drawing buffer. 207 | 208 | __fadeToColorBy(void* leds, uint16_t count, uint32_t color, uint8_t fadeAmt);__ 209 | 210 | Fades drawing array towards the background color by amount. It is used just like FastLED's 211 | fadeToBlackBy(). 212 | 213 | **Example:** 214 | 215 | fadeToColorBy( myCube, 16*16*16, 0xFF8000, 20 ); //fades towards orange by 20/255ths 216 | 217 | 218 | __drawSquare(void* leds, uint16_t planeY, uint16_t planeX, int yCorner, int xCorner, uint size, uint32_t color);__ 219 | 220 | Safely draws box in given RGB color on LED plane. cornerY and cornerX specify the lower left 221 | corner of the box. It is safe to specify -cornerY, -cornerX, and safe to draw a box which only 222 | partially fits on LED plane. 223 | 224 | **Example:** 225 | 226 | //draws a red 10x10 square on the 3rd 16x16 plane of myCube, lower left corner anchored at 227 | //col = row = 4. 228 | drawSquare( &myCube[2][0][0], 16, 16, 4, 4, 10, 0xFF0000 ); 229 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | ### Release 1.1.0 2 | * Changed default LED waveform timing to fix conflict with Teensy Audio library and possibly other DMA-enabled apps. 3 | More relaxed default timing allows ObjectFLED to work out-of-the-box with more LED chips as well. 4 | * Eliminated using both overclock factor and pulse timing specs in same .begin() function call. Either specify OC factor, 5 | or pulse timing values, but not both. See mouseover help or ObjectFLED.h for the updated .begin() signatures. Only those 6 | using the full form of begin(OC_Factor, THTL, T0H, T1H, Latch_Delay) will need to update their .begin() call. 7 | * Changed how OC factor is applied to waveform timing. Originally, OC factor was applied to equally shrink TH_TL, T0H, 8 | and T1H. Now, OC factor applies to TH_TL and T0H equally, but only reduces T1H by 1/3 of the amount. This is because 9 | LED chips have a fixed threshold for when a H pulse is a 0 or a 1. This change yielded slightly better overclockability 10 | in testing with WS2812B chips._ 11 | 12 | ### Release 1.0.3 13 | * Added support for GBR, BGR color formats. 14 | 15 | ### Release 1.0.2 16 | * Set DSE=3 on output pins improved LED overclocking by 7% (boot default DSE=6). 17 | 18 | ### Release 1.0.1 19 | * Updated includes to replace include Arduino.h. 20 | * Started making releases, added INSTALLATION.md file. 21 | * Tweakage to readme. 22 | -------------------------------------------------------------------------------- /examples/Cylon/Cylon.ino: -------------------------------------------------------------------------------- 1 | //Cylon demo for OctoFLED Raw (without FastLED) 2 | #include 3 | 4 | // How many leds in your strip? 5 | #define NUM_LEDS 32 6 | 7 | // For led chips like Neopixels, which have a data line, ground, and power, you just 8 | // need to define DATA_PIN. Or list of pins for parallel connections to your LED device. 9 | uint8_t DATA_PIN[1] = { 17 }; 10 | // Define the array of leds, 3 bytes per 11 | uint8_t leds[NUM_LEDS*3]; 12 | // Define the display object with 8-pixel serpentine on 8x8 plane 13 | ObjectFLED cylon( NUM_LEDS, leds, CORDER_GRB, sizeof(DATA_PIN), DATA_PIN, 8); 14 | void setup() { 15 | cylon.begin(); //make display object ready at default LED clock rate 800 KHz 16 | cylon.setBrightness(84); 17 | } 18 | 19 | const uint8_t dotColor[3] = { 0xFF, 0, 0 }; //R, G, B 20 | const int delayT = 25; 21 | const int fadeAmt = 40; 22 | void loop() { 23 | for( int i=0; i=0; i-=3) { 31 | memcpy(&leds[i], &dotColor, 3); 32 | cylon.show(); 33 | fadeToColorBy(leds, NUM_LEDS, 0 ,fadeAmt); 34 | delay(delayT); 35 | } 36 | delay(delayT); 37 | } //loop() 38 | -------------------------------------------------------------------------------- /examples/DemoReel2D/DemoReel2D.ino: -------------------------------------------------------------------------------- 1 | /// @file DemoReel100.ino 2 | /// @brief FastLED "100 lines of code" demo reel, showing off some effects 3 | /// @example DemoReel100.ino 4 | // FastLED "100-lines-of-code" demo reel, showing just a few 5 | // of the kinds of animation patterns you can quickly and easily 6 | // compose using FastLED. 7 | // 8 | // This example also shows one easy way to define multiple 9 | // animations patterns and have them automatically rotate. 10 | // 11 | // -Mark Kriegsman, December 2014 12 | // 13 | // Ported to use ObjectFLED, input brightness via Serial Nov 2024 by Kurt Funderburg 14 | #include 15 | #include 16 | 17 | #define NUM_LEDS 8 //per row 18 | #define NUM_STRIPS 8 //should be even multiple of NUM_PINS; 24 = 4 rows per pin 19 | uint BRIGHTNESS = 6; 20 | const uint NUM_PINS = 2; 21 | //uint8_t ports[NUM_PINS] = { 1, 8, 14, 17, 24, 29 }; 22 | uint8_t ports[NUM_PINS] = { 17, 24 }; 23 | uint8_t ports2[4] = { 1, 8, 14, 29 }; 24 | 25 | //Create 2 display objects using the same drawing array to display image on 2 devices 26 | CRGB plane[NUM_STRIPS][NUM_LEDS]; 27 | //8x8 grid with data connection broken between 4th & 5th rows to drive with 2 pins parallel 28 | ObjectFLED dispPlane(NUM_LEDS*NUM_STRIPS, plane, CORDER_GRB, NUM_PINS, ports, 8); 29 | //4 4x4 grids of LEDs on my breadboard (for better results, replace this with device of same 30 | // or even multiples of the first device dimensions) 31 | ObjectFLED dispPlane2(NUM_LEDS*NUM_STRIPS, plane, CORDER_RGB, 4, ports2, 4); 32 | 33 | void setup() { 34 | Serial.begin(1000000); 35 | Serial.print("*********************************************\n"); 36 | Serial.print("* DemoReel1002D.ino *\n"); 37 | Serial.print("*********************************************\n"); 38 | Serial.printf("CPU Speed: %d MHz Temp: %.1f C %.1f F Brightness %d\n", F_CPU_ACTUAL / 1000000, \ 39 | tempmonGetTemp(), tempmonGetTemp() * 9.0 / 5.0 + 32, BRIGHTNESS); 40 | Serial.println("Enter brightness level at any time via serial monitor."); 41 | 42 | //start both display objects 43 | dispPlane.begin(); 44 | dispPlane2.begin(); 45 | dispPlane.setBrightness(BRIGHTNESS); 46 | dispPlane2.setBrightness(BRIGHTNESS); 47 | } // setup() 48 | 49 | 50 | // List of patterns to cycle through. Each is defined as a separate function below. 51 | typedef void (*SimplePatternList[])(); 52 | SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm }; 53 | 54 | uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current 55 | static uint8_t gHue = 0; // rotating "base color" used by many of the patterns 56 | uint strIdx = 0, strLen = 8; //for reading brightness int from serial 57 | char serInput[8], x; 58 | 59 | void loop() { 60 | // Call the current pattern function once, updating the 'plane' array 61 | gPatterns[gCurrentPatternNumber](); 62 | 63 | 64 | // send the 'plane' array out to both displays 65 | dispPlane.show(); 66 | dispPlane2.show(); 67 | delay(40); // too see the glitter 68 | 69 | // do some periodic updates 70 | EVERY_N_MILLISECONDS(30) { 71 | gHue++; 72 | } // slowly cycle the "base color" through the rainbow 73 | 74 | EVERY_N_SECONDS(1) { // check/read serial for new brightness# 75 | strIdx = 0; 76 | while(Serial.available() && strIdx < strLen) { 77 | if ((x = Serial.read()) != -1) { 78 | if(x >= '0' && x <= '9') { serInput[strIdx++] = x; } //store it if a digit 79 | } 80 | } //while R bytes 81 | Serial.clear(); // got my 8B, anything else must be cat on keybaord 82 | 83 | if(strIdx > 0) { //str > int, then set brightness 84 | BRIGHTNESS = 0; 85 | for(uint i=0; i 32864) || (phase < 0)) phaseInc = -phaseInc; 140 | phase += phaseInc; 141 | } 142 | 143 | void bpm() { 144 | // colored stripes pulsing at a defined Beats-Per-Minute (BPM) 145 | uint8_t BeatsPerMinute = 30; 146 | CRGBPalette16 palette = PartyColors_p; 147 | uint8_t beat = beatsin8(BeatsPerMinute, 64, 255); 148 | for (int j = 0; j < NUM_STRIPS; j++) { 149 | for (int i = 0; i < NUM_LEDS; i++) { 150 | plane[j][i] = ColorFromPalette(palette, i*32+j*32, beat); 151 | } 152 | } 153 | } 154 | 155 | void juggle() { 156 | // eight colored dots, weaving in and out of sync with each other 157 | fadeToBlackBy(plane[0], NUM_LEDS * NUM_STRIPS, 15); 158 | uint8_t dothue = 0; 159 | for (int k = 0; k < NUM_STRIPS; k++) { 160 | for (int i = 0; i < NUM_LEDS; i++) { 161 | plane[k][beatsin16(40, 0, NUM_LEDS - 1)] = CHSV(dothue, 255, 255); 162 | dothue += 2; 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /examples/ObjectFLEDExperiment/ObjectFLEDExperiment.ino: -------------------------------------------------------------------------------- 1 | /* OctoExperiment.ino - Test program to show revolving electron around edges of 2 | 3 4x4 planes laid out horizontally (on a breadboard) while fading the nucleus 3 | (an 8x8 plane) between blue and orange in sync with electron. 4 | Kurt Funderburg - Nov 2024 5 | 6 | The 3 planes are each connected to a Teensy pin, driven in parallel, while the 7 | 8x8 plane is separated into 2 groups of 4 rows, each group connected to a pin. 8 | You can alter the variables to reflect the geometry and signal path for your LED 9 | devices. 10 | 11 | In code, the 3 planes are stored in CRGB electronLED[Z][Y][X]. The nucleus plane 12 | is CRGB nucleusLED[Y][X]. This allows for intuitive loop control, and ObjectFLED 13 | requires drawing buffer arrays to be in row-major order. 14 | */ 15 | #include 16 | #include 17 | 18 | //3x4x4 YF923 RGB "cube" actually 3 planes laid out horizontally in 4x12. 19 | #define PIX_PER_ROW 4 20 | #define ROWS_PER_PLANE 4 21 | #define NUM_PLANES 3 22 | #define NUM_PINS 3 23 | #define NUM_LEDS (PIX_PER_ROW * ROWS_PER_PLANE * NUM_PLANES) 24 | const byte pinList[NUM_PINS] = {1, 8, 14}; 25 | const int config = CORDER_RGB; 26 | CRGB electronLED[NUM_PLANES][ROWS_PER_PLANE][PIX_PER_ROW]; 27 | ObjectFLED electron(NUM_LEDS, electronLED, config, NUM_PINS, pinList, PIX_PER_ROW); 28 | 29 | //8x8 WS2812B GRB plane from Amazon 30 | #define PIX_PER_ROW2 8 31 | #define NUM_ROWS2 8 32 | #define NUM_PINS2 2 33 | #define NUM_LEDS2 (PIX_PER_ROW2 * NUM_ROWS2) 34 | const byte pinList2[NUM_PINS2] = {17, 24}; 35 | const int config2 = CORDER_GRB; 36 | CRGB nucleusLED[NUM_ROWS2][PIX_PER_ROW2]; //Octo assumes this buffer size/type 37 | ObjectFLED nucleus(NUM_LEDS2, nucleusLED, config2, NUM_PINS2, pinList2, 8); 38 | 39 | const uint32_t background = 0xFFFF00; 40 | const uint32_t drawColor = 0x0000FF; 41 | const uint8_t brightness = 4; 42 | const int delayT = 60; // mS loop delay 43 | const int fade = 50; // fade electron 44 | int fade2 = 20; // fade nucleus 45 | uint planeColor1 = 0x0000FF; 46 | uint planeColor2 = 0xff7000; 47 | uint planeColor = planeColor1; 48 | void setup() { 49 | Serial.begin(1000000); 50 | Serial.println("OctoExperiment"); 51 | 52 | //"nucleus" plane 8x8 fades back and forth from orange to blue driven by 2 pins 53 | nucleus.begin(); //use default LED timing: 800 KHz (1250 nS) with 75 uS latch delay 54 | nucleus.setBrightness(brightness); 55 | nucleus.setBalance(0xdae0ff); 56 | fill_solid(nucleusLED[0], NUM_LEDS2, planeColor2); 57 | nucleus.show(); 58 | 59 | //"electron orbit" around 3 4x4 planes driven by 3 pins 60 | //1.68 OC factor with these timing values are max OC for YF923's I found posing as WS2812B's 61 | electron.begin(1.6, 72); 62 | electron.setBrightness(brightness); 63 | electron.setBalance(0xdae0ff); 64 | fill_solid(electronLED[0][0], NUM_LEDS, background); 65 | electron.show(); 66 | //while(Serial.read()==-1); 67 | } 68 | 69 | void loop() { 70 | for (int i=0; i=0; i--) { //electron down right edge of right plane 91 | electronLED[NUM_PLANES-1][i][PIX_PER_ROW-1] = drawColor; 92 | electron.show(); 93 | fadeToColorBy(electronLED[0][0], NUM_LEDS, background, fade); 94 | drawSquare(nucleusLED, NUM_ROWS2, PIX_PER_ROW2, 4 - i - 1, 4 - i - 1, i * 2 + 2, 0x800000); 95 | nucleus.show(); 96 | fadeToColorBy(nucleusLED[0], NUM_LEDS2, planeColor, fade2); 97 | delay(delayT); 98 | } 99 | for (int j=NUM_PLANES-1; j>=0; j--) { //electron left across bottom of planes 100 | for (int i=PIX_PER_ROW-1; i>=0; i--) { 101 | electronLED[j][0][i] = drawColor; 102 | electron.show(); 103 | fadeToColorBy(electronLED[0][0], NUM_LEDS, background, fade); 104 | nucleus.show(); 105 | fadeToColorBy(nucleusLED[0], NUM_LEDS2, planeColor, fade2); 106 | delay(delayT); 107 | } 108 | } 109 | 110 | if(planeColor == planeColor2) { planeColor = planeColor1; fade2 = fade2 / 2; } 111 | else { planeColor = planeColor2; fade2 = fade2 * 2; } 112 | } // loop() 113 | -------------------------------------------------------------------------------- /examples/ObjectFLEDTest/ObjectFLEDTest.ino: -------------------------------------------------------------------------------- 1 | /* ObjectFLEDTest.ino - Test program to compute back-to-back and one-time refresh 2 | rates for various LED configurations 3 | Kurt Funderburg - Nov 2024 4 | */ 5 | #include 6 | #include 7 | 8 | //configure your test cube with display object = leds, draw buffer = testCube[Z][Y][X]. 9 | //configure a separate cube to pre-clear any physical LEDs you actually have connected 10 | // with display object = blanks, draw buffer = blankLeds[Z][Y][X]. 11 | //notice that with ObjectFLED you can define 2 display objects to drive the same physical 12 | // object on the same physical pins. 13 | const int PIX_PER_ROW = 16, 14 | NUM_ROWS = 16, 15 | NUM_PLANES = 16, 16 | NUM_CHANNELS = 16, 17 | COLOR_ORDER = RGB, 18 | STD_OUT_BAUD = 100000; 19 | const CRGB background = 0x505000; 20 | byte pinList[NUM_CHANNELS] = {1, 8, 14, 17, 24, 29, 20, 0, 15, 16, 18, 19, 21, 22, 23, 25}; 21 | byte pinListBlank[7] = {1, 8, 14, 17, 24, 29, 20}; 22 | const int config = CORDER_RGB; 23 | 24 | CRGB testCube[NUM_PLANES][NUM_ROWS][PIX_PER_ROW]; 25 | CRGB blankLeds[7][8][8]; 26 | ObjectFLED leds(PIX_PER_ROW * NUM_ROWS * NUM_PLANES, testCube, config, NUM_CHANNELS, pinList); 27 | ObjectFLED blanks(7*8*8, blankLeds, config, 7, pinListBlank); 28 | 29 | CRGB leds2[PIX_PER_ROW * NUM_ROWS * NUM_PLANES]; // FastLED draw buffer 30 | uint startT=0, stopT=0; 31 | void setup() { 32 | Serial.begin(STD_OUT_BAUD); 33 | Serial.print("*********************************************\n"); 34 | Serial.print("* ObjectFLEDTest.ino *\n"); 35 | Serial.print("*********************************************\n"); 36 | Serial.printf("CPU speed: %d MHz Temp: %.1f C %.1f F Serial baud: %.1f MHz\n", \ 37 | F_CPU_ACTUAL / 1000000, \ 38 | tempmonGetTemp(), tempmonGetTemp() * 9.0 / 5.0 + 32, \ 39 | 800000 * 1.6 / 1000000.0); 40 | 41 | //blank all leds/ all channels 42 | 43 | //Second parallel pin group Teensy 4.0: 10,12,11,13,6,9,32,8,7 44 | //Third parallel pin group Teensy 4.0: 37, 36, 35, 34, 39, 38, 28, 31, 30 from FastLED docs 45 | // FastLED.addLeds(leds2, PIX_PER_ROW * NUM_ROWS * NUM_PLANES / NUM_CHANNELS); //4-strip parallel 46 | // FastLED.setBrightness(5); 47 | // FastLED.setCorrection(0xB0E0FF); 48 | fill_solid(blankLeds[0][0], 7*8*8, 0x0); 49 | fill_solid(testCube[0][0], NUM_PLANES * NUM_ROWS * PIX_PER_ROW, background); 50 | leds.begin(); // Use alternate forms of this function to overclock 51 | leds.setBrightness(6); 52 | leds.setBalance(0xDAE0FF); 53 | 54 | blanks.begin(); //default timing 800KHz LED clk, 75uS latch 55 | blanks.show(); 56 | while(Serial.read() == -1); 57 | } //setup() 58 | 59 | void loop() { 60 | startT = micros(); 61 | // 20 calls 62 | leds.show(); 63 | leds.show(); 64 | leds.show(); 65 | leds.show(); 66 | leds.show(); 67 | leds.show(); 68 | leds.show(); 69 | leds.show(); 70 | leds.show(); 71 | leds.show(); 72 | leds.show(); 73 | leds.show(); 74 | leds.show(); 75 | leds.show(); 76 | leds.show(); 77 | leds.show(); 78 | leds.show(); 79 | leds.show(); 80 | leds.show(); 81 | leds.show(); 82 | stopT = micros(); 83 | Serial.printf("LEDs/channel: %d ObjectFLED avg/20 time: %d uS %f fps\n", \ 84 | PIX_PER_ROW*NUM_ROWS*NUM_PLANES/NUM_CHANNELS, 85 | (stopT - startT) / 20, 20000000.0 / (stopT - startT)); 86 | while (Serial.read() == -1); 87 | 88 | startT = micros(); 89 | leds.show(); 90 | stopT = micros(); 91 | Serial.printf("LEDs/channel: %d ObjectFLED 1 frame time: %d uS %f fps\n", \ 92 | PIX_PER_ROW*NUM_ROWS*NUM_PLANES/NUM_CHANNELS, 93 | (stopT - startT), 1000000.0 / (stopT - startT)); 94 | while (Serial.read() == -1); 95 | return; 96 | 97 | 98 | /* 99 | startT = micros(); 100 | // 20 calls 101 | FastLED.show(); 102 | FastLED.show(); 103 | FastLED.show(); 104 | FastLED.show(); 105 | FastLED.show(); 106 | FastLED.show(); 107 | FastLED.show(); 108 | FastLED.show(); 109 | FastLED.show(); 110 | FastLED.show(); 111 | FastLED.show(); 112 | FastLED.show(); 113 | FastLED.show(); 114 | FastLED.show(); 115 | FastLED.show(); 116 | FastLED.show(); 117 | FastLED.show(); 118 | FastLED.show(); 119 | FastLED.show(); 120 | FastLED.show(); 121 | stopT = micros(); 122 | Serial.printf("LEDs/channel: %d FastLED avg/20 time: %d uS %f fps\n", \ 123 | PIX_PER_ROW*NUM_ROWS*NUM_PLANES/NUM_CHANNELS, 124 | (stopT - startT) / 20, 20000000.0 / (stopT - startT)); 125 | //Serial.printf("CPU temp: %.1f C %.1f F\n", tempmonGetTemp(), tempmonGetTemp() * 9.0 / 5.0 + 32); 126 | while (Serial.read() == -1); 127 | 128 | startT = micros(); 129 | FastLED.show(); 130 | stopT = micros(); 131 | Serial.printf("LEDs/channel: %d FastLED 1 frame time: %d uS %f fps\n", \ 132 | PIX_PER_ROW*NUM_ROWS*NUM_PLANES/NUM_CHANNELS, 133 | (stopT - startT), 1000000.0 / (stopT - startT)); 134 | while (Serial.read() == -1); 135 | */ 136 | } //loop() 137 | -------------------------------------------------------------------------------- /examples/PlasmaAnimation/PlasmaAnimation.ino: -------------------------------------------------------------------------------- 1 | //PlazINT - Fast Plasma Generator using Integer Math Only 2 | //Edmund "Skorn" Horn 3 | //March 4,2013 4 | /* PlasmaAnimation.ino - ported the above code to use ObjectFLED display library. 5 | Kurt Funderburg - Nov 2024 */ 6 | #include 7 | #include 8 | 9 | //ObjectFLED Defn. Stuff 10 | #define COLS_LEDs 8 // X 11 | #define ROWS_LEDs 20 // Y 12 | #define NUM_PINS 5 // CHANNELS 13 | #define NUM_LEDS (COLS_LEDs * ROWS_LEDs) 14 | 15 | byte pinList[NUM_PINS] = {1, 8, 14, 17, 24}; 16 | const int config = CORDER_GRB; 17 | int delayT = 20; // mS loop delay 18 | 19 | CRGB drawingMemory[NUM_LEDS]; 20 | ObjectFLED leds(NUM_LEDS, drawingMemory, config, NUM_PINS, pinList, COLS_LEDs); 21 | 22 | //Byte val 2PI Cosine Wave, offset by 1 PI 23 | //supports fast trig calcs and smooth LED fading/pulsing. 24 | uint8_t const cos_wave[256] PROGMEM = 25 | {0,0,0,0,1,1,1,2,2,3,4,5,6,6,8,9,10,11,12,14,15,17,18,20,22,23,25,27,29,31,33,35,38,40,42, 26 | 45,47,49,52,54,57,60,62,65,68,71,73,76,79,82,85,88,91,94,97,100,103,106,109,113,116,119, 27 | 122,125,128,131,135,138,141,144,147,150,153,156,159,162,165,168,171,174,177,180,183,186, 28 | 189,191,194,197,199,202,204,207,209,212,214,216,218,221,223,225,227,229,231,232,234,236, 29 | 238,239,241,242,243,245,246,247,248,249,250,251,252,252,253,253,254,254,255,255,255,255, 30 | 255,255,255,255,254,254,253,253,252,252,251,250,249,248,247,246,245,243,242,241,239,238, 31 | 236,234,232,231,229,227,225,223,221,218,216,214,212,209,207,204,202,199,197,194,191,189, 32 | 186,183,180,177,174,171,168,165,162,159,156,153,150,147,144,141,138,135,131,128,125,122, 33 | 119,116,113,109,106,103,100,97,94,91,88,85,82,79,76,73,71,68,65,62,60,57,54,52,49,47,45, 34 | 42,40,38,35,33,31,29,27,25,23,22,20,18,17,15,14,12,11,10,9,8,6,6,5,4,3,2,2,1,1,1,0,0,0,0 35 | }; 36 | 37 | 38 | //Gamma Correction Curve 39 | uint8_t const exp_gamma[256] PROGMEM = 40 | {0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,3,3,3, 41 | 4,4,4,4,4,5,5,5,5,5,6,6,6,7,7,7,7,8,8,8,9,9,9,10,10,10,11,11,12,12,12,13,13,14,14,14,15,15, 42 | 16,16,17,17,18,18,19,19,20,20,21,21,22,23,23,24,24,25,26,26,27,28,28,29,30,30,31,32,32,33, 43 | 34,35,35,36,37,38,39,39,40,41,42,43,44,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60, 44 | 61,62,63,64,65,66,67,68,70,71,72,73,74,75,77,78,79,80,82,83,84,85,87,89,91,92,93,95,96,98, 45 | 99,100,101,102,105,106,108,109,111,112,114,115,117,118,120,121,123,125,126,128,130,131,133, 46 | 135,136,138,140,142,143,145,147,149,151,152,154,156,158,160,162,164,165,167,169,171,173,175, 47 | 177,179,181,183,185,187,190,192,194,196,198,200,202,204,207,209,211,213,216,218,220,222,225, 48 | 227,229,232,234,236,239,241,244,246,249,251,253,254,255 49 | }; 50 | 51 | 52 | void setup() 53 | { 54 | // Serial.begin(1000000); 55 | leds.setBrightness(10); 56 | leds.setBalance(0xdae0ff); 57 | leds.begin(); 58 | leds.show(); 59 | } 60 | 61 | unsigned long frameCount=25500; // arbitrary seed to calculate the three time displacement variables t,t2,t3 62 | void loop() { 63 | frameCount++ ; 64 | uint16_t t = fastCosineCalc((42 * frameCount)/100); //time displacement - fiddle with these til it looks good... 65 | uint16_t t2 = fastCosineCalc((35 * frameCount)/100); 66 | uint16_t t3 = fastCosineCalc((38 * frameCount)/100); 67 | 68 | //implements 8-LED / ROW serpentine 69 | for (uint8_t y = 0; y < ROWS_LEDs; y++) { 70 | int pixelIndex; 71 | pixelIndex = y * COLS_LEDs; 72 | 73 | for (uint8_t x = 0; x < COLS_LEDs ; x++) { 74 | //Calculate 3 seperate plasma waves, one for each color channel 75 | uint8_t r = fastCosineCalc(((x << 3) + (t >> 1) + fastCosineCalc((t2 + (y << 3))))); 76 | uint8_t g = fastCosineCalc(((y << 3) + t + fastCosineCalc(((t3 >> 2) + (x << 3))))); 77 | uint8_t b = fastCosineCalc((y << 3) + t2 + fastCosineCalc(t + (x << 3))); 78 | if(r+g < 80) { b = max(b, 80); } 79 | //uncomment the following to enable gamma correction 80 | r=pgm_read_byte_near(exp_gamma+r)/2 + r/2; 81 | g=pgm_read_byte_near(exp_gamma+g)/2 + g/2; 82 | b=pgm_read_byte_near(exp_gamma+b)/2 + b/2; 83 | drawingMemory[pixelIndex] = ((r << 16) & 0xff0000) | ((g << 8) & 0xff00) | b; 84 | pixelIndex++; 85 | } //for X 86 | } //for Y 87 | //digitalWrite(13, HIGH); 88 | leds.show(); 89 | //digitalWrite(13, LOW); 90 | delay(delayT); 91 | } // loop() 92 | 93 | 94 | inline uint8_t fastCosineCalc( uint16_t preWrapVal) 95 | { 96 | uint8_t wrapVal = (preWrapVal % 255); 97 | if (wrapVal<0) wrapVal=255+wrapVal; 98 | return (pgm_read_byte_near(cos_wave+wrapVal)); 99 | } 100 | -------------------------------------------------------------------------------- /examples/UsesFastLED/UsesFastLED.ino: -------------------------------------------------------------------------------- 1 | /* ObjectFLED sample program showing how to use the FastLED built-in ObjectFLED DMA engine, 2 | without loading ObjectFLED.h. 5 LED strings are configured to show blue-cylon lights and 3 | display show() framerate based on 20 back-to-back calls. Tested with FastLED 3.9.11 & 3.9.13. 4 | 5 | When using FastLED without ObjectFLED.h, each string/pin is added seperately in setup(), and 6 | show() displays all strings at once. Strings can be of varying length, but you cannot show 7 | strings independently. Only default waveform timing is used. */ 8 | //#define FASTLED_USES_OBJECTFLED //not required for FastLED 3.9.12 and later 9 | #define FASTLED_OVERCLOCK 1.5 10 | #include 11 | 12 | #define PIX_PER_STR 32 13 | #define NUM_STR 5 14 | CRGB leds[NUM_STR][PIX_PER_STR]; //2D array for all strings, or use separate string arrays 15 | 16 | void setup() { 17 | // pin# 0-4, each with it's own LED string and array row 18 | FastLED.addLeds(leds[0], PIX_PER_STR); 19 | FastLED.addLeds(leds[1], PIX_PER_STR); 20 | FastLED.addLeds(leds[2], PIX_PER_STR); 21 | FastLED.addLeds(leds[3], PIX_PER_STR); 22 | FastLED.addLeds(leds[4], PIX_PER_STR); 23 | fill_solid(leds[0], NUM_STR * PIX_PER_STR, 0x0); //blanks all strings 24 | FastLED.setBrightness(10); 25 | 26 | Serial.begin(100000); 27 | } //setup() 28 | 29 | int delayT = 30; 30 | void loop() { 31 | //RIGHT blue-cylon pattern on all NUM_STR strings 32 | for (uint x=0; x=0; x--) { 43 | for (int y=NUM_STR-1; y>=0; y--) { 44 | leds[y][x] = CRGB::Blue; 45 | } 46 | FastLED.show(); 47 | fadeToBlackBy(leds[0], NUM_STR * PIX_PER_STR, 32); 48 | delay(delayT); 49 | } 50 | 51 | //speed test 20 show() calls 52 | uint totalT, startT = micros(); 53 | FastLED.show(); 54 | FastLED.show(); 55 | FastLED.show(); 56 | FastLED.show(); 57 | FastLED.show(); 58 | FastLED.show(); 59 | FastLED.show(); 60 | FastLED.show(); 61 | FastLED.show(); 62 | FastLED.show(); 63 | FastLED.show(); 64 | FastLED.show(); 65 | FastLED.show(); 66 | FastLED.show(); 67 | FastLED.show(); 68 | FastLED.show(); 69 | FastLED.show(); 70 | FastLED.show(); 71 | FastLED.show(); 72 | FastLED.show(); 73 | totalT = micros() - startT; 74 | Serial.printf("Uses FastLED Avg T of 20 : %.1f uS FPS: %.1f\n", totalT / 20.0, 20.0 / totalT * 1000000); 75 | while (Serial.read()==-1); 76 | } //loop() 77 | -------------------------------------------------------------------------------- /examples/UsesObjectFLED/UsesObjectFLED.ino: -------------------------------------------------------------------------------- 1 | /* ObjectFLED sample program showing how to use ObjectFLED.h with FastLEDs. 5 LED strings 2 | are configured to show blue-cylon lights and display show() framerate based on 20 back-to-back 3 | calls. Tested with FastLED 3.9.11 & 3.9.13. 4 | 5 | When using FastLED with ObjectFLED.h, an ordered array of pin numbers is passed to the ObjectFLED 6 | instance along with the 2D array for all 5 strings, and show() displays all strings at once. 7 | If strings are of varying length, then the 2D array holds the largest string, and extra data in the 8 | array for shorter strings is effectively ignored, even if sent to the string. 9 | 10 | You can optionally define separate 1D arrays for varying size strings with a separate ObjectFLED 11 | instance for each string. With this approach, each string has it's own show(), get/setBrightness(), 12 | get/setBalance(), overclock, custom waveform timing, and color order. */ 13 | #include //must include this before FastLED.h 14 | #include 15 | 16 | #define PIX_PER_STR 32 17 | #define NUM_STR 5 18 | CRGB leds[NUM_STR][PIX_PER_STR]; //2D array for all strings, or use separate string arrays 19 | // pin# 0-4, each with it's own LED string and array row 20 | uint8_t pinList[NUM_STR] = {0, 1, 17, 3, 4}; 21 | ObjectFLED dispLeds(PIX_PER_STR * NUM_STR, leds, CORDER_RGB, 5, pinList ); 22 | 23 | void setup() { 24 | dispLeds.begin(1.5); //1.5 Overclock Factor 25 | fill_solid(leds[0], NUM_STR * PIX_PER_STR, 0x0); //blanks all strings 26 | dispLeds.setBrightness(10); 27 | Serial.begin(100000); 28 | } //setup() 29 | 30 | int delayT = 30; 31 | void loop() { 32 | //RIGHT blue-cylon pattern on all NUM_STR strings 33 | for (uint x=0; x=0; x--) { 44 | for (int y=NUM_STR-1; y>=0; y--) { 45 | leds[y][x] = CRGB::Blue; 46 | } 47 | dispLeds.show(); 48 | fadeToBlackBy(leds[0], NUM_STR * PIX_PER_STR, 32); 49 | delay(delayT); 50 | } 51 | 52 | //speed test 20 show() calls 53 | uint totalT, startT = micros(); 54 | dispLeds.show(); 55 | dispLeds.show(); 56 | dispLeds.show(); 57 | dispLeds.show(); 58 | dispLeds.show(); 59 | dispLeds.show(); 60 | dispLeds.show(); 61 | dispLeds.show(); 62 | dispLeds.show(); 63 | dispLeds.show(); 64 | dispLeds.show(); 65 | dispLeds.show(); 66 | dispLeds.show(); 67 | dispLeds.show(); 68 | dispLeds.show(); 69 | dispLeds.show(); 70 | dispLeds.show(); 71 | dispLeds.show(); 72 | dispLeds.show(); 73 | dispLeds.show(); 74 | totalT = micros() - startT; 75 | Serial.printf("Uses ObjectFLED Avg T of 20 : %.1f uS FPS: %.1f\n", totalT / 20.0, 20.0 / totalT * 1000000); 76 | while (Serial.read()==-1); 77 | } //loop() 78 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ObjectFLED KEYWORD1 2 | begin KEYWORD2 3 | show KEYWORD2 4 | busy KEYWORD2 5 | setBrightness KEYWORD2 6 | setBalance KEYWORD2 7 | getBrightness KEYWORD2 8 | getBalance KEYWORD2 9 | fadeToColorBy KEYWORD2 10 | drawSquare KEYWORD2 11 | CORDER_RGB LITERAL1 12 | CORDER_RBG LITERAL1 13 | CORDER_GRB LITERAL1 14 | CORDER_GBR LITERAL1 15 | CORDER_BRG LITERAL1 16 | CORDER_BGR LITERAL1 17 | CORDER_RGBW LITERAL1 18 | CORDER_RBGW LITERAL1 19 | CORDER_GRBW LITERAL1 20 | CORDER_GBRW LITERAL1 21 | CORDER_BRGW LITERAL1 22 | CORDER_BGRW LITERAL1 23 | CORDER_WRGB LITERAL1 24 | CORDER_WRBG LITERAL1 25 | CORDER_WGRB LITERAL1 26 | CORDER_WGBR LITERAL1 27 | CORDER_WBRG LITERAL1 28 | CORDER_WBGR LITERAL1 29 | CORDER_RWGB LITERAL1 30 | CORDER_RWBG LITERAL1 31 | CORDER_GWRB LITERAL1 32 | CORDER_GWBR LITERAL1 33 | CORDER_BWRG LITERAL1 34 | CORDER_BWGR LITERAL1 35 | CORDER_RGWB LITERAL1 36 | CORDER_RBWG LITERAL1 37 | CORDER_GRWB LITERAL1 38 | CORDER_GBWR LITERAL1 39 | CORDER_BRWG LITERAL1 40 | CORDER_BGWR LITERAL1 41 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ObjectFLED 2 | version=1.1.0 3 | author=Kurt Funderburg 4 | maintainer=Kurt Funderburg 5 | sentence=Independently configure and display to various LED devices in one sketch with parallel DMA-driven LED output. 6 | paragraph=Uses DMA transfer on all 40 or 55 digital pins of Teensy 4.0 or 4.1 to drive digital LEDs for display of FastLED arrays of CRGB, or other drawing buffer in RGB 3-byte format. The show() function is non-blocking, returning control to the graphics drawing code in just 6% of the time it takes to complete buffer transmission to an LED string, plane, or cube (tested with WS2812B, 1.6 overclock factor, 256 LED per pin x 16 pins = 4096 LEDs total @204 fps). 7 | category=Display 8 | url=https://github.com/KurtMF/ObjectFLED.git 9 | architectures=* 10 | 11 | --------------------------------------------------------------------------------