FastLED 3.9.15
Loading...
Searching...
No Matches
OjectFLED.cpp.hpp
Go to the documentation of this file.
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/*
33Teensy 4.0 pin - port assignments
34GPIO6List = { 0, 1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 } //12 top, 4 bottom
35GPIO7List = { 6, 7, 8, 9, 10, 11, 12, 13, 32 } //8 top, 1 bottom
36GPIO8List = { 28, 30, 31, 34, 35, 36, 37, 38, 39 } //0 top, 9 bottom
37GOIO9List = { 2, 3, 4, 5, 29, 33 } //4 top, 2 bottom
38Teensy 4.1 pin - port assignments
39GPIO6List = { 0, 1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 38, 39, 40, 41 } //20 top, 0 bottom
40GPIO7List = { 6, 7, 8, 9, 10, 11, 12, 13, 32, 34, 35, 36, 37 } //13 top, 0 bottom
41GPIO8List = { 28, 30, 31, 42, 43, 44, 45, 46, 47 } //3 top, 6 bottom
42GOIO9List = { 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#include "platforms/arm/teensy/is_teensy.h"
61
62#ifndef FL_IS_TEENSY_4X
63// Do nothing for other platforms.
64#else
65#include "ObjectFLED.h"
68#include "fl/log/log.h"
69
70#ifndef MIN
71#define MIN(a,b) ((a)<(b)?(a):(b))
72#endif
73
74#ifndef MAX
75#define MAX(a,b) ((a)>(b)?(a):(b))
76#endif
77
78namespace fl {
79
80volatile bool dma_first;
81
82
83ObjectFLED::ObjectFLED(uint16_t numLEDs, void *drawBuf, uint8_t config, uint8_t numPins, \
84 const uint8_t *pinList, uint8_t serpentine) {
85 serpNumber = serpentine;
86 drawBuffer = drawBuf;
87 params = config;
88 if (numPins > NUM_DIGITAL_PINS) numPins = NUM_DIGITAL_PINS;
89 numpinsLocal = numPins;
90 stripLen = numLEDs / numpinsLocal;
91 memcpy(pinlist, pinList, numpinsLocal);
92 if ((params & 0x3F) < 6) {
93 frameBufferLocal = new uint8_t[numLEDs * 3];
94 numbytesLocal = stripLen * 3; // RGB formats
95 }
96 else {
97 frameBufferLocal = new uint8_t[numLEDs * 4];
98 numbytesLocal = stripLen * 4; // RGBW formats
99 }
100} // ObjectFLED constructor
101
102
103extern "C" void xbar_connect(unsigned int input, unsigned int output); // in pwm.c
104static volatile uint32_t *standard_gpio_addr(volatile uint32_t *fastgpio) {
105 return (volatile uint32_t *)((uint32_t)fastgpio - 0x01E48000);
106}
107
108
109void ObjectFLED::begin(uint16_t latchDelay) {
110 LATCH_DELAY = latchDelay;
111 begin();
112}
113
114
115void ObjectFLED::begin(uint16_t t1, uint16_t t2, uint16_t t3, uint16_t latchDelay) {
116 // Convert 3-phase timing (T1, T2, T3) to ObjectFLED's internal format
117 // See fl/clockless/timing_conversion.h for details
118 // T1 = T0H (high time for '0' bit)
119 // T1 + T2 = T1H (high time for '1' bit)
120 // T1 + T2 + T3 = total period
121 uint16_t clk_ns = t1 + t2 + t3;
122 uint16_t t0h_ns = t1;
123 uint16_t t1h_ns = t1 + t2;
124 beginInternal(clk_ns, t0h_ns, t1h_ns, latchDelay);
125}
126
127void ObjectFLED::beginInternal(uint16_t period, uint16_t t0h, uint16_t t1h, uint16_t latchDelay) {
128 TH_TL = period;
129 T0H = t0h;
130 T1H = t1h;
131 LATCH_DELAY = latchDelay;
132 begin();
133}
134
135
136// INPUT stripLen, frameBuffer, params, numPins, pinList
137// GPIOR bits set for pins[i] -> bitmask, pin_bitnum[i], pin_offset[i]
138// init timers, xbar to DMA, DMA bitdata -> GPIOR; clears frameBuffer (total LEDs * 3 bytes)
139void ObjectFLED::begin(void) {
140 auto& dma = ObjectFLEDDmaManager::getInstance();
141
142 // Set each pin's bitmask bit, store offset & bit# for pin
143 uint32_t tempBitmask[4] = {0};
144 uint8_t validPinCount = 0;
145 for (uint32_t i=0; i < numpinsLocal; i++) {
146 uint8_t pin = pinlist[i];
147
148 // Validate pin using validation helper
149 auto validation = objectfled::validate_teensy4_pin(pin);
150 if (!validation.valid) {
151 FL_WARN("================================================================================");
152 FL_WARN("FASTLED ERROR: Pin " << (int)pin << " is INVALID and has been disabled");
153 FL_WARN(validation.error_message);
154 FL_WARN("================================================================================");
155 continue;
156 }
157
158 // Check for warnings (pin is valid but may have issues)
159 if (validation.error_message != nullptr) {
160 FL_WARN("================================================================================");
161 FL_WARN("FASTLED WARNING: Pin " << (int)pin << " may have issues");
162 FL_WARN(validation.error_message);
163 FL_WARN("================================================================================");
164 }
165
166 uint8_t bit = digitalPinToBit(pin); // pin's bit index in word port DR
167 // which GPIO R controls this pin: 0-3 map to GPIO6-9 then map to DMA compat GPIO1-4
168 uint8_t offset = ((uint32_t)portOutputRegister(pin) - (uint32_t)&GPIO6_DR) >> 14;
169 if (offset > 3) {
170 FL_WARN("================================================================================");
171 FL_WARN("FASTLED ERROR: Pin " << (int)pin << " does not map to GPIO6-9 (offset=" << (int)offset << ")");
172 FL_WARN("This pin may be a ground/power/read-only pin - strip disabled");
173 FL_WARN("================================================================================");
174 continue;
175 }
176
177 validPinCount++;
178 pin_bitnumLocal[i] = bit; //local copy for context switch
179 pin_offsetLocal[i] = offset; //local copy for context switch
180 uint32_t mask = 1 << bit; //mask32 = bit set @position in GPIO DR
181 tempBitmask[offset] |= mask; //bitmask32[0..3] = collective pin bit masks for each GPIO DR
182 //bit7:6 SPEED; bit 5:3 DSE; bit0 SRE (default SPEED = 0b10; def. DSE = 0b110)
183 *portControlRegister(pin) &= ~0xF9; //clear SPEED, DSE, SRE
184 *portControlRegister(pin) |= ((OUTPUT_PAD_SPEED & 0x3) << 6) | \
185 ((OUTPUT_PAD_DSE & 0x7) << 3); //DSE = 0b011 for LED overclock
186 //clear pin bit in IOMUX_GPR26 to map GPIO6-9 to GPIO1-4 for DMA
187 *(&IOMUXC_GPR_GPR26 + offset) &= ~mask;
188 *standard_gpio_addr(portModeRegister(pin)) |= mask; //GDIR? bit flag set output mode
189 }
190
191 // Check if any valid pins were configured
192 if (validPinCount == 0) {
193 FL_WARN("================================================================================");
194 FL_WARN("FASTLED CRITICAL ERROR: No valid pins configured!");
195 FL_WARN("All " << (int)numpinsLocal << " pins failed validation.");
196 FL_WARN("ObjectFLED driver is disabled - no LEDs will be updated.");
197 FL_WARN("================================================================================");
198 return;
199 }
200
201 if (validPinCount < numpinsLocal) {
202 FL_WARN("================================================================================");
203 FL_WARN("FASTLED WARNING: Only " << (int)validPinCount << " of " << (int)numpinsLocal << " pins are valid");
204 FL_WARN("Strips on invalid pins will not function.");
205 FL_WARN("================================================================================");
206 }
207
208 //stash context for multi-show
209 memcpy(bitmaskLocal, tempBitmask, 16);
210
211 arm_dcache_flush_delete(bitmaskLocal, sizeof(bitmaskLocal)); //can't DMA from cached memory
212
213 // Set up 3 timers to create waveform timing events
214 comp1load[0] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)TH_TL);
215 comp1load[1] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T0H);
216 comp1load[2] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T1H);
217 TMR4_ENBL &= ~7;
218 TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR;
219 TMR4_CSCTRL0 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN;
220 TMR4_CNTR0 = 0;
221 TMR4_LOAD0 = 0;
222 TMR4_COMP10 = comp1load[0];
223 TMR4_CMPLD10 = comp1load[0];
224 TMR4_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(3);
225 TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
226 TMR4_CNTR1 = 0;
227 TMR4_LOAD1 = 0;
228 TMR4_COMP11 = comp1load[1]; // T0H
229 TMR4_CMPLD11 = comp1load[1];
230 TMR4_CTRL1 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3);
231 TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
232 TMR4_CNTR2 = 0;
233 TMR4_LOAD2 = 0;
234 TMR4_COMP12 = comp1load[2]; // T1H
235 TMR4_CMPLD12 = comp1load[2];
236 TMR4_CTRL2 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3);
237
238 // route the timer outputs through XBAR to edge trigger DMA request: only 4 mappings avail.
239 CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON);
240 xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_DMA_CH_MUX_REQ30);
241 xbar_connect(XBARA1_IN_QTIMER4_TIMER1, XBARA1_OUT_DMA_CH_MUX_REQ31);
242 xbar_connect(XBARA1_IN_QTIMER4_TIMER2, XBARA1_OUT_DMA_CH_MUX_REQ94);
243 XBARA1_CTRL0 = XBARA_CTRL_STS1 | XBARA_CTRL_EDGE1(3) | XBARA_CTRL_DEN1 |
244 XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0;
245 XBARA1_CTRL1 = XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0;
246
247 // configure DMA channels
248 dma.dma1.begin();
249 dma.dma1.TCD->SADDR = dma.bitmask; // source 4*32b GPIO pin mask
250 dma.dma1.TCD->SOFF = 8; // bytes offset added to SADDR after each transfer
251 // SMOD(4) low bits of SADDR to update with adds of SOFF
252 // SSIZE(3) code for 64 bit transfer size DSIZE(2) code for 32 bit transfer size
253 dma.dma1.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2);
254 dma.dma1.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | // Dest minor loop offsetting enable
255 DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
256 DMA_TCD_NBYTES_MLOFFYES_NBYTES(16); // #bytes to tansfer, offsetting enabled
257 dma.dma1.TCD->SLAST = 0; // add to SADDR after xfer
258 dma.dma1.TCD->DADDR = &GPIO1_DR_SET;
259 dma.dma1.TCD->DOFF = 16384; //&GPIO1_DR_SET + DOFF = next &GPIO2_DR_SET
260 dma.dma1.TCD->CITER_ELINKNO = numbytesLocal * 8; // CITER outer loop count (linking disabled) = # LED bits to write
261 dma.dma1.TCD->DLASTSGA = -65536; // add to DADDR after xfer
262 dma.dma1.TCD->BITER_ELINKNO = numbytesLocal * 8; // Beginning CITER (not decremented by transfer)
263 dma.dma1.TCD->CSR = DMA_TCD_CSR_DREQ; // channel ERQ field cleared when minor loop completed
264 dma.dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_0); // only 4 XBAR1 triggers (DMA MUX mapping)
265
266 dma.dma2next.TCD->SADDR = dma.bitdata; //uint32_t bitdata[BYTES_PER_DMA*64]
267 dma.dma2next.TCD->SOFF = 8;
268 dma.dma2next.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_DSIZE(2);
269 dma.dma2next.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE |
270 DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
271 DMA_TCD_NBYTES_MLOFFYES_NBYTES(16);
272 dma.dma2next.TCD->SLAST = 0;
273 dma.dma2next.TCD->DADDR = &GPIO1_DR_CLEAR;
274 dma.dma2next.TCD->DOFF = 16384;
275 dma.dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
276 dma.dma2next.TCD->DLASTSGA = (int32_t)(dma.dma2next.TCD);
277 dma.dma2next.TCD->BITER_ELINKNO = BYTES_PER_DMA * 8;
278 dma.dma2next.TCD->CSR = 0;
279
280 dma.dma2.begin();
281 dma.dma2 = dma.dma2next; // copies TCD
282 dma.dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_1);
283 dma.dma2.attachInterrupt(isr);
284
285 dma.dma3.begin();
286 dma.dma3.TCD->SADDR = dma.bitmask;
287 dma.dma3.TCD->SOFF = 8;
288 dma.dma3.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2);
289 dma.dma3.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE |
290 DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
291 DMA_TCD_NBYTES_MLOFFYES_NBYTES(16);
292 dma.dma3.TCD->SLAST = 0;
293 dma.dma3.TCD->DADDR = &GPIO1_DR_CLEAR;
294 dma.dma3.TCD->DOFF = 16384;
295 dma.dma3.TCD->CITER_ELINKNO = numbytesLocal * 8;
296 dma.dma3.TCD->DLASTSGA = -65536;
297 dma.dma3.TCD->BITER_ELINKNO = numbytesLocal * 8;
298 dma.dma3.TCD->CSR = DMA_TCD_CSR_DREQ | DMA_TCD_CSR_DONE;
299 dma.dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_2);
300} // begin()
301
302
303//*dest = *bitdata + pin offset
304//*pixels = pin's block in frameBuffer
305//mask = pin's bit position in GPIOR
306//set a pin's mask32 for each color bit=0 at every 4*words32 in bitdata+offset
307void fillbits(uint32_t *dest, const uint8_t *pixels, int n, uint32_t mask) {
308 do {
309 uint8_t pix = *pixels++;
310 if (!(pix & 0x80)) *dest |= mask;
311 dest += 4;
312 if (!(pix & 0x40)) *dest |= mask;
313 dest += 4;
314 if (!(pix & 0x20)) *dest |= mask;
315 dest += 4;
316 if (!(pix & 0x10)) *dest |= mask;
317 dest += 4;
318 if (!(pix & 0x08)) *dest |= mask;
319 dest += 4;
320 if (!(pix & 0x04)) *dest |= mask;
321 dest += 4;
322 if (!(pix & 0x02)) *dest |= mask;
323 dest += 4;
324 if (!(pix & 0x01)) *dest |= mask;
325 dest += 4;
326 } while (--n > 0);
327}
328
329
331 uint32_t j = 0;
332 int jChange = -3;
333 if (serp == 0) { // use faster loops if no serp
334 switch (params & 0x3F) {
335 case CORDER_RGBW: // R,G,B = R,G,B - MIN(R,G,B); W = MIN(R,G,B)
336 for (uint16_t i = 0; i < (numbytesLocal * numpinsLocal); i += 4) {
337 uint8_t minRGB = MIN(*((uint8_t*)drawBuffer + j) * rLevel / 65025, \
338 *((uint8_t*)drawBuffer + j + 1) * rLevel / 65025);
339 minRGB = MIN(minRGB, *((uint8_t*)drawBuffer + j + 2) * rLevel / 65025);
340 *(frameBufferLocal + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025 - minRGB;
341 *(frameBufferLocal + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
342 *(frameBufferLocal + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
343 *(frameBufferLocal + i + 3) = minRGB;
344 j += 3;
345 } //for(leds in drawbuffer)
346 break;
347 case CORDER_GBR:
348 for (uint16_t i = 0; i < (numbytesLocal * numpinsLocal); i += 3) {
349 *(frameBufferLocal + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
350 *(frameBufferLocal + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
351 *(frameBufferLocal + i + 1) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
352 j += 3;
353 } //for(leds in drawbuffer)
354 break;
355 case CORDER_BGR:
356 for (uint16_t i = 0; i < (numbytesLocal * numpinsLocal); i += 3) {
357 *(frameBufferLocal + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
358 *(frameBufferLocal + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
359 *(frameBufferLocal + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
360 j += 3;
361 } //for(leds in drawbuffer)
362 break;
363 case CORDER_BRG:
364 for (uint16_t i = 0; i < (numbytesLocal * numpinsLocal); i += 3) {
365 *(frameBufferLocal + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
366 *(frameBufferLocal + i + 2) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
367 *(frameBufferLocal + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
368 j += 3;
369 } //for(leds in drawbuffer)
370 break;
371 case CORDER_GRB:
372 for (uint16_t i = 0; i < (numbytesLocal * numpinsLocal); i += 3) {
373 *(frameBufferLocal + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
374 *(frameBufferLocal + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
375 *(frameBufferLocal + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
376 j += 3;
377 } //for(leds in drawbuffer)
378 break;
379 case CORDER_RGB:
380 default:
381 for (uint16_t i = 0; i < (numbytesLocal * numpinsLocal); i += 3) {
382 *(frameBufferLocal + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
383 *(frameBufferLocal + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
384 *(frameBufferLocal + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
385 j += 3;
386 } //for(leds in drawbuffer)
387 } // switch()
388 } else { //serpentine
389 switch (params & 0x3F) {
390 case CORDER_RGBW: // R,G,B = R,G,B - MIN(R,G,B); W = MIN(R,G,B)
391 for (uint16_t i = 0; i < (numbytesLocal * numpinsLocal); i += 4) {
392 uint8_t minRGB = MIN(*((uint8_t*)drawBuffer + j) * rLevel / 65025, \
393 * ((uint8_t*)drawBuffer + j + 1) * rLevel / 65025);
394 minRGB = MIN(minRGB, *((uint8_t*)drawBuffer + j + 2) * rLevel / 65025);
395 if (i % (serp * 4) == 0) {
396 if (jChange < 0) { j = i / 4 * 3; jChange = 3; }
397 else { j = (i / 4 + serp - 1) * 3; jChange = -3; }
398 }
399 *(frameBufferLocal + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025 - minRGB;
400 *(frameBufferLocal + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
401 *(frameBufferLocal + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
402 *(frameBufferLocal + i + 3) = minRGB;
403 j += jChange;
404 } //for(leds in drawbuffer)
405 break;
406 case CORDER_GBR:
407 for (uint16_t i = 0; i < (numbytesLocal * numpinsLocal); i += 3) {
408 if (i % (serp * 3) == 0) {
409 if (jChange < 0) { j = i; jChange = 3; }
410 else { j = i + (serp - 1) * 3; jChange = -3; }
411 }
412 *(frameBufferLocal + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
413 *(frameBufferLocal + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
414 *(frameBufferLocal + i + 1) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
415 j += 3;
416 } //for(leds in drawbuffer)
417 break;
418 case CORDER_BGR:
419 for (uint16_t i = 0; i < (numbytesLocal * numpinsLocal); i += 3) {
420 if (i % (serp * 3) == 0) {
421 if (jChange < 0) { j = i; jChange = 3; }
422 else { j = i + (serp - 1) * 3; jChange = -3; }
423 }
424 *(frameBufferLocal + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
425 *(frameBufferLocal + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
426 *(frameBufferLocal + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
427 j += 3;
428 } //for(leds in drawbuffer)
429 break;
430 case CORDER_BRG:
431 for (uint16_t i = 0; i < (numbytesLocal * numpinsLocal); i += 3) {
432 if (i % (serp * 3) == 0) {
433 if (jChange < 0) { j = i; jChange = 3; }
434 else { j = i + (serp - 1) * 3; jChange = -3; }
435 }
436 *(frameBufferLocal + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
437 *(frameBufferLocal + i + 2) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
438 *(frameBufferLocal + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
439 j += jChange;
440 } //for(leds in drawbuffer)
441 break;
442 case CORDER_GRB:
443 for (uint16_t i = 0; i < (numbytesLocal * numpinsLocal); i += 3) {
444 if (i % (serp * 3) == 0) {
445 if (jChange < 0) { j = i; jChange = 3; }
446 else { j = i + (serp - 1) * 3; jChange = -3; }
447 }
448 *(frameBufferLocal + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
449 *(frameBufferLocal + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
450 *(frameBufferLocal + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
451 j += jChange;
452 } //for(leds in drawbuffer)
453 break;
454 case CORDER_RGB:
455 default:
456 for (uint16_t i = 0; i < (numbytesLocal * numpinsLocal); i += 3) {
457 if (i % (serp * 3) == 0) {
458 if (jChange < 0) { j = i; jChange = 3; }
459 else { j = i + (serp - 1) * 3; jChange = -3; }
460 }
461 *(frameBufferLocal + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
462 *(frameBufferLocal + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
463 *(frameBufferLocal + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
464 j += jChange;
465 } //for(leds in drawbuffer)
466 } // switch()
467 } // else serpentine
468} //genFrameBuffer()
469
470
471// pre-show prior transfer wait, copies drawBuffer -> frameBuffer
472// resets timers, clears pending DMA reqs
473// fills bitdata[BYTES_PER_DMA * 64 * 4 bytes] from frameBuffer with 4-block bitmasks for 0's in led data
474// 4 word32s for each bit in (led data)/pin = 16 * 8 = 96 bitdata bytes for each LED byte: 288 bytes / LED
475// launches DMA with IRQ activation to reload bitdata from frameBuffer
476void ObjectFLED::show(void) {
477 auto& dma = ObjectFLEDDmaManager::getInstance();
478
479 // Acquire DMA (blocks if another instance transmitting)
480 dma.acquire(this);
481
482 //Restore context if needed
483 if (dma.frameBuffer != frameBufferLocal) {
484 dma.numpins = numpinsLocal;
485 dma.frameBuffer = frameBufferLocal;
486 dma.numbytes = numbytesLocal;
487 memcpy(dma.bitmask, bitmaskLocal, 16);
488 memcpy(dma.pin_bitnum, pin_bitnumLocal, numpinsLocal);
489 memcpy(dma.pin_offset, pin_offsetLocal, numpinsLocal);
490 arm_dcache_flush_delete(dma.bitmask, sizeof(dma.bitmask)); //can't DMA from cached memory
491 // Restore 3 timers to create waveform timing events
492 TMR4_COMP10 = comp1load[0];
493 TMR4_CMPLD10 = comp1load[0];
494 TMR4_COMP11 = comp1load[1]; // T0H
495 TMR4_CMPLD11 = comp1load[1];
496 TMR4_COMP12 = comp1load[2]; // T1H
497 TMR4_CMPLD12 = comp1load[2];
498 //restore DMA loop control
499 dma.dma1.TCD->CITER_ELINKNO = dma.numbytes * 8; // CITER outer loop count (linking disabled) = # LED bits to write
500 dma.dma1.TCD->BITER_ELINKNO = dma.numbytes * 8; // Beginning CITER (not decremented by transfer)
501 dma.dma3.TCD->CITER_ELINKNO = dma.numbytes * 8;
502 dma.dma3.TCD->BITER_ELINKNO = dma.numbytes * 8;
503 } //done restoring context
504
506
507 // disable timers
508 uint16_t enable = TMR4_ENBL;
509 TMR4_ENBL = enable & ~7;
510
511 // force all timer outputs to logic low
512 TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR;
513 TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
514 TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
515
516 // clear any prior pending DMA requests
517 XBARA1_CTRL0 |= XBARA_CTRL_STS1 | XBARA_CTRL_STS0;
518 XBARA1_CTRL1 |= XBARA_CTRL_STS0;
519
520 // fill the DMA transmit buffer
521 memset(dma.bitdata, 0, sizeof(dma.bitdata)); //BYTES_PER_DMA * 64 words32
522 uint32_t count = dma.numbytes; //bytes per strip
523 if (count > BYTES_PER_DMA*2) count = BYTES_PER_DMA*2;
524 dma.framebuffer_index = count; //ptr to framebuffer last byte output
525
526 //Sets each pin mask in bitdata32[BYTES_PER_DMA*64] for every 0 bit of pin's frameBuffer block bytes
527 for (uint32_t i=0; i < dma.numpins; i++) { //for each pin
528 fillbits(dma.bitdata + dma.pin_offset[i], (uint8_t *)dma.frameBuffer + i*dma.numbytes,
529 count, 1<<dma.pin_bitnum[i]);
530 }
531 arm_dcache_flush_delete(dma.bitdata, count * 128); // don't need bitdata in cache for DMA
532
533 // set up DMA transfers
534 if (dma.numbytes <= BYTES_PER_DMA*2) {
535 dma.dma2.TCD->SADDR = dma.bitdata;
536 dma.dma2.TCD->DADDR = &GPIO1_DR_CLEAR;
537 dma.dma2.TCD->CITER_ELINKNO = count * 8;
538 dma.dma2.TCD->CSR = DMA_TCD_CSR_DREQ;
539 } else {
540 dma.dma2.TCD->SADDR = dma.bitdata;
541 dma.dma2.TCD->DADDR = &GPIO1_DR_CLEAR;
542 dma.dma2.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
543 dma.dma2.TCD->CSR = 0;
544 dma.dma2.TCD->CSR = DMA_TCD_CSR_INTMAJOR | DMA_TCD_CSR_ESG;
545 dma.dma2next.TCD->SADDR = dma.bitdata + BYTES_PER_DMA*32;
546 dma.dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
547 if (dma.numbytes <= BYTES_PER_DMA*3) {
548 dma.dma2next.TCD->CSR = DMA_TCD_CSR_ESG;
549 } else {
550 dma.dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR;
551 }
552 dma_first = true;
553 }
554 dma.dma3.clearComplete();
555 dma.dma1.enable();
556 dma.dma2.enable();
557 dma.dma3.enable();
558
559 // initialize timers
560 TMR4_CNTR0 = 0;
561 TMR4_CNTR1 = comp1load[0] + 1;
562 TMR4_CNTR2 = comp1load[0] + 1;
563
564 // wait for last LED reset to finish
565 while (micros() - update_begin_micros < dma.numbytes * 8 * TH_TL / 1000 + LATCH_DELAY);
566
567 // start everything running!
568 TMR4_ENBL = enable | 7;
570
571 // Release DMA (transmission continues asynchronously)
572 dma.release(this);
573} // show()
574
575
576//INPUT: dma2, dma2next, bitdata, framebuffer_inedex, numpins, numbytes, pin_offset[], pin_bitnum[]
577//Reads next block of framebuffer -> fillbits() -> bitdata
578//Checks for last block to transfer, next to last, or not to update dma2next major loop
579void ObjectFLED::isr(void)
580{
581 auto& dma = ObjectFLEDDmaManager::getInstance();
582
583 // first ack the interrupt
584 dma.dma2.clearInterrupt();
585
586 // fill (up to) half the transmit buffer with new fillbits(frameBuffer data)
587 //digitalWriteFast(12, HIGH);
588 uint32_t *dest;
589 if (dma_first) {
590 dma_first = false;
591 dest = dma.bitdata;
592 } else {
593 dma_first = true;
594 dest = dma.bitdata + BYTES_PER_DMA*32;
595 }
596 memset(dest, 0, sizeof(dma.bitdata)/2);
597 uint32_t index = dma.framebuffer_index;
598 uint32_t count = dma.numbytes - dma.framebuffer_index;
599 if (count > BYTES_PER_DMA) count = BYTES_PER_DMA;
600 dma.framebuffer_index = index + count;
601 for (int i=0; i < dma.numpins; i++) {
602 fillbits(dest + dma.pin_offset[i], (uint8_t *)dma.frameBuffer + index + i*dma.numbytes,
603 count, 1<<dma.pin_bitnum[i]);
604 }
605 arm_dcache_flush_delete(dest, count * 128);
606 //digitalWriteFast(12, LOW);
607
608 // queue it for the next DMA transfer
609 dma.dma2next.TCD->SADDR = dest;
610 dma.dma2next.TCD->CITER_ELINKNO = count * 8;
611 uint32_t remain = dma.numbytes - (index + count);
612 if (remain == 0) {
613 dma.dma2next.TCD->CSR = DMA_TCD_CSR_DREQ;
614 } else if (remain <= BYTES_PER_DMA) {
615 dma.dma2next.TCD->CSR = DMA_TCD_CSR_ESG;
616 } else {
617 dma.dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR;
618 }
619} // isr()
620
621
622int ObjectFLED::busy(void)
623{
624 auto& dma = ObjectFLEDDmaManager::getInstance();
625 if (micros() - update_begin_micros < dma.numbytes * TH_TL / 1000 * 8 + LATCH_DELAY) {
626 return 1;
627 }
628 return 0;
629}
630
631
632void ObjectFLED::setBrightness(uint8_t brightLevel) {
633 brightness = brightLevel;
634 rLevel = brightness * (colorBalance >> 16);
635 gLevel = brightness * ((colorBalance >> 8) & 0xFF);
636 bLevel = brightness * (colorBalance & 0xFF);
637 }
638
639
640void ObjectFLED::setBalance(uint32_t balMask) {
641 colorBalance = balMask & 0xFFFFFF;
642 rLevel = brightness * (colorBalance >> 16);
643 gLevel = brightness * ((colorBalance >> 8) & 0xFF);
644 bLevel = brightness * (colorBalance & 0xFF);
645}
646
647
648//Fades CRGB array towards the background color by amount.
649void fadeToColorBy(void* leds, uint16_t count, uint32_t color, uint8_t fadeAmt) {
650 for (uint32_t x = 0; x < count * 3; x += 3) {
651 //fade red
652 *((uint8_t*)leds + x) = (( *((uint8_t*)leds + x) * (1 + (255 - fadeAmt))) >> 8) + \
653 (( ((color >> 16) & 0xFF) * (1 + fadeAmt)) >> 8);
654 //fade green
655 *((uint8_t*)leds + x + 1) = (( *((uint8_t*)leds + x + 1) * (1 + (255 - fadeAmt))) >> 8) + \
656 (( ((color >> 8) & 0xFF) * (1 + fadeAmt)) >> 8);
657 //fade blue
658 *((uint8_t*)leds + x + 2) = (( *((uint8_t*)leds + x + 2) * (1 + (255 - fadeAmt))) >> 8) + \
659 (( (color & 0xFF) * (1 + fadeAmt)) >> 8);
660 }
661} //fadeToColorBy()
662
663
664// Safely draws box even if partially offscreen on 2D CRGB array
665void drawSquare(void* leds, uint16_t planeY, uint16_t planeX, int yCorner, int xCorner, uint32_t size, uint32_t color) {
666 if (size != 0) { size--; }
667 else { return; }
668 for (int x = xCorner; x <= xCorner + (int)size; x++) {
669 // if validX { if validY+S {draw Y+S,X}; if validY {draw Y, X} }
670 if ((x >= 0) && (x < planeX)) { //valid X
671 if ((yCorner >= 0) && (yCorner < planeY)) {
672 *((uint8_t*)leds + (yCorner * planeX + x) * 3) = ((color >> 16) & 0xFF);
673 *((uint8_t*)leds + (yCorner * planeX + x) * 3 + 1) = ((color >> 8) & 0xFF);
674 *((uint8_t*)leds + (yCorner * planeX + x) * 3 + 2) = (color & 0xFF);
675 }
676 // FastLED #2726: `size` is uint32_t so `yCorner + size` promotes
677 // to unsigned, making the `>= 0` clause always true (-Wtype-limits)
678 // and breaking the bounds check when yCorner is negative. Cast
679 // `size` to int to keep the arithmetic signed (matches the
680 // for-loop bound `x <= xCorner + (int)size` above).
681 if ((yCorner + (int)size >= 0) && (yCorner + (int)size < planeY)) {
682 *((uint8_t*)leds + ((yCorner + (int)size) * planeX + x) * 3) = ((color >> 16) & 0xFF);
683 *((uint8_t*)leds + ((yCorner + (int)size) * planeX + x) * 3 + 1) = ((color >> 8) & 0xFF);
684 *((uint8_t*)leds + ((yCorner + (int)size) * planeX + x) * 3 + 2) = (color & 0xFF);
685 }
686 } //if valid x
687 } //for x
688 for (int y = yCorner; y <= yCorner + (int)size; y++) {
689 if ((y >= 0) && (y < planeY)) { //valid y
690 if ((xCorner >= 0) && (xCorner < planeX)) {
691 *((uint8_t*)leds + (xCorner + y * planeX) * 3) = ((color >> 16) & 0xFF);
692 *((uint8_t*)leds + (xCorner + y * planeX) * 3 + 1) = ((color >> 8) & 0xFF);
693 *((uint8_t*)leds + (xCorner + y * planeX) * 3 + 2) = (color & 0xFF);
694 }
695 // FastLED #2726: same -Wtype-limits fix as the yCorner branch above.
696 if ((xCorner + (int)size >= 0) && (xCorner + (int)size < planeX)) {
697 *((uint8_t*)leds + (xCorner + (int)size + y * planeX) * 3) = ((color >> 16) & 0xFF);
698 *((uint8_t*)leds + (xCorner + (int)size + y * planeX) * 3 + 1) = ((color >> 8) & 0xFF);
699 *((uint8_t*)leds + (xCorner + (int)size + y * planeX) * 3 + 2) = (color & 0xFF);
700 }
701 } //if valid y
702 } //for y
703} // drawSquare()
704
705
706// ============================================================================
707// ObjectFLED Member Functions (moved from header)
708// ============================================================================
709
711 // Wait for prior xmission to end, don't need to wait for latch time before deleting buffer
712 while (micros() - update_begin_micros < numbytesLocal * 8 * TH_TL / 1000 + 5);
713 delete frameBufferLocal;
714}
715
717 ObjectFLEDDmaManager::getInstance().waitForCompletion();
718}
719
721 return brightness;
722}
723
725 return colorBalance;
726}
727
728} // namespace fl
729
730
731#endif // FL_IS_TEENSY_4X
fl::CRGB leds[NUM_LEDS]
#define CORDER_GRB
Definition ObjectFLED.h:65
#define CORDER_BGR
Definition ObjectFLED.h:68
#define CORDER_RGBW
Definition ObjectFLED.h:69
#define BYTES_PER_DMA
Definition ObjectFLED.h:61
#define OUTPUT_PAD_DSE
Definition ObjectFLED.h:51
#define CORDER_BRG
Definition ObjectFLED.h:67
#define OUTPUT_PAD_SPEED
Definition ObjectFLED.h:52
#define CORDER_GBR
Definition ObjectFLED.h:66
#define CORDER_RGB
Definition ObjectFLED.h:63
void * drawBuffer
Definition ObjectFLED.h:183
uint8_t * frameBufferLocal
Definition ObjectFLED.h:169
uint8_t brightness
Definition ObjectFLED.h:177
uint8_t pin_bitnumLocal[NUM_DIGITAL_PINS]
Definition ObjectFLED.h:199
uint8_t pinlist[NUM_DIGITAL_PINS]
Definition ObjectFLED.h:187
uint32_t update_begin_micros
Definition ObjectFLED.h:176
uint32_t colorBalance
Definition ObjectFLED.h:178
int busy(void)
uint32_t getBalance()
void setBalance(uint32_t)
uint16_t LATCH_DELAY
Definition ObjectFLED.h:193
void genFrameBuffer(uint32_t)
ObjectFLED(uint16_t numLEDs, void *drawBuf, uint8_t config, uint8_t numPins, const uint8_t *pinList, uint8_t serpentine=0)
uint32_t gLevel
Definition ObjectFLED.h:180
uint8_t getBrightness()
void waitForDmaToFinish()
uint32_t bitmaskLocal[4]
Definition ObjectFLED.h:196
static void isr(void)
uint16_t stripLen
Definition ObjectFLED.h:185
uint8_t serpNumber
Definition ObjectFLED.h:189
void setBrightness(uint8_t)
uint16_t TH_TL
Definition ObjectFLED.h:190
uint32_t rLevel
Definition ObjectFLED.h:179
void begin(void)
void show(void)
uint32_t numbytesLocal
Definition ObjectFLED.h:198
uint16_t comp1load[3]
Definition ObjectFLED.h:188
uint8_t numpinsLocal
Definition ObjectFLED.h:197
void beginInternal(uint16_t, uint16_t, uint16_t, uint16_t=300)
uint8_t pin_offsetLocal[NUM_DIGITAL_PINS]
Definition ObjectFLED.h:200
uint32_t bLevel
Definition ObjectFLED.h:181
#define MIN(a, b)
Definition coder.h:64
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
#define FL_WARN(X)
Definition log.h:276
Centralized logging categories for FastLED hardware interfaces and subsystems.
void * memcpy(void *dest, const void *src, size_t n) FL_NOEXCEPT
fl::u32 uint32_t
Definition s16x16x4.h:219
fl::u16 uint16_t
Definition s16x16x4.h:214
void * memset(void *s, int c, size_t n) FL_NOEXCEPT
void drawSquare(void *, uint16_t, uint16_t, int, int, uint32_t, uint32_t)
fl::u32 micros()
Universal microsecond timer - returns microseconds since system startup.
fl::i32 int32_t
Definition s16x16x4.h:220
void fadeToColorBy(void *, uint16_t, uint32_t, uint8_t)
unsigned char uint8_t
Definition s16x16x4.h:209
Base definition for an LED controller.
Definition crgb.hpp:179
PinValidationResult validate_teensy4_pin(uint8_t pin)