FastLED 3.9.7
Loading...
Searching...
No Matches
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/*
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*/
56
57#ifndef __IMXRT1062__
58// Do nothing for other platforms.
59#else
60#include "ObjectFLED.h"
61
62#ifndef MIN
63#define MIN(a,b) ((a)<(b)?(a):(b))
64#endif
65
66#ifndef MAX
67#define MAX(a,b) ((a)>(b)?(a):(b))
68#endif
69
70volatile uint32_t framebuffer_index = 0; //isr()
71uint8_t* ObjectFLED::frameBuffer; //isr()
72uint32_t ObjectFLED::numbytes; //isr()
73uint8_t ObjectFLED::numpins; //isr()
74uint8_t ObjectFLED::pin_bitnum[NUM_DIGITAL_PINS]; //isr()
75uint8_t ObjectFLED::pin_offset[NUM_DIGITAL_PINS]; //isr()
76uint32_t ObjectFLED::bitdata[BYTES_PER_DMA * 64] __attribute__((used, aligned(32))); //isr()
77uint32_t ObjectFLED::bitmask[4] __attribute__((used, aligned(32)));
78
79DMASetting ObjectFLED::dma2next;
80DMAChannel ObjectFLED::dma1;
81DMAChannel ObjectFLED::dma2;
82DMAChannel ObjectFLED::dma3;
83volatile bool dma_first;
84
85
86ObjectFLED::ObjectFLED(uint16_t numLEDs, void *drawBuf, uint8_t color_order, uint8_t numPins, \
87 const uint8_t *pinList, uint8_t serpentine) {
88 serpNumber = serpentine;
89 drawBuffer = drawBuf;
90 params = color_order;
91 if (numPins > NUM_DIGITAL_PINS) numPins = NUM_DIGITAL_PINS;
92 numpins = numPins; //static/isr
93 stripLen = numLEDs / numpins;
94 memcpy(pinlist, pinList, numpins);
95 if ((params & 0x3F) < 6) {
96 frameBuffer = new uint8_t[numLEDs * 3]; //static/isr
97 numbytes = stripLen * 3; // RGB formats //static/isr
98 }
99 else {
100 frameBuffer = new uint8_t[numLEDs * 4]; //static/isr
101 numbytes = stripLen * 4; // RGBW formats //static/isr
102 }
103
104 numpinsLocal = numPins;
105 frameBufferLocal = frameBuffer;
106 numbytesLocal = numbytes;
107} // ObjectFLED constructor
108
109
110extern "C" void xbar_connect(unsigned int input, unsigned int output); // in pwm.c
111static volatile uint32_t *standard_gpio_addr(volatile uint32_t *fastgpio) {
112 return (volatile uint32_t *)((uint32_t)fastgpio - 0x01E48000);
113}
114
115
116void ObjectFLED::begin(float OCF) {
117 OC_FACTOR = OCF;
118 begin();
119}
120
121
122void ObjectFLED::begin(float OCF, uint16_t latchDelay) {
123 OC_FACTOR = OCF;
124 LATCH_DELAY = latchDelay;
125 begin();
126}
127
128
129void ObjectFLED::begin(float OCF, uint16_t period, uint16_t t0h, uint16_t t1h, uint16_t latchDelay) {
130 OC_FACTOR = OCF;
131 TH_TL = period;
132 T0H = t0h;
133 T1H = t1h;
134 LATCH_DELAY = latchDelay;
135 begin();
136}
137
138
139// INPUT stripLen, frameBuffer, params, numPins, pinList
140// GPIOR bits set for pins[i] -> bitmask, pin_bitnum[i], pin_offset[i]
141// init timers, xbar to DMA, DMA bitdata -> GPIOR; clears frameBuffer (total LEDs * 3 bytes)
142void ObjectFLED::begin(void) {
143 numpins = numpinsLocal; //needed to compute pin mask/offset & bitmask since static for isr
144 // Set each pin's bitmask bit, store offset & bit# for pin
145 memset(bitmask, 0, sizeof(bitmask));
146 for (uint32_t i=0; i < numpins; i++) {
147 uint8_t pin = pinlist[i];
148 if (pin >= NUM_DIGITAL_PINS) continue; // ignore illegal pins
149 uint8_t bit = digitalPinToBit(pin); // pin's bit index in word port DR
150 // which GPIO R controls this pin: 0-3 map to GPIO6-9 then map to DMA compat GPIO1-4
151 uint8_t offset = ((uint32_t)portOutputRegister(pin) - (uint32_t)&GPIO6_DR) >> 14;
152 if (offset > 3) continue; //ignore unknown pins
153 pin_bitnum[i] = bit; //static/isr
154 pin_offset[i] = offset; //static/isr
155 uint32_t mask = 1 << bit; //mask32 = bit set @position in GPIO DR
156 bitmask[offset] |= mask; //bitmask32[0..3] = collective pin bit masks for each GPIO DR
157 //bit7:6 SPEED; bit 5:3 DSE; bit0 SRE (default SPEED = 0b10; def. DSE = 0b110)
158 *portControlRegister(pin) &= ~0xF9; //clear SPEED, DSE, SRE
159 *portControlRegister(pin) |= ((OUTPUT_PAD_SPEED & 0x3) << 6) | \
160 ((OUTPUT_PAD_DSE & 0x7) << 3); //DSE = 0b011 for LED overclock
161 //clear pin bit in IOMUX_GPR26 to map GPIO6-9 to GPIO1-4 for DMA
162 *(&IOMUXC_GPR_GPR26 + offset) &= ~mask;
163 *standard_gpio_addr(portModeRegister(pin)) |= mask; //GDIR? bit flag set output mode
164 }
165 //stash context for multi-show
166 memcpy(bitmaskLocal, bitmask, 16);
167 memcpy(pin_bitnumLocal, pin_bitnum, numpins);
168 memcpy(pin_offsetLocal, pin_offset, numpins);
169
170 arm_dcache_flush_delete(bitmask, sizeof(bitmask)); //can't DMA from cached memory
171
172 // Set up 3 timers to create waveform timing events
173 comp1load[0] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)TH_TL / OC_FACTOR );
174 comp1load[1] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T0H / OC_FACTOR );
175 comp1load[2] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T1H / OC_FACTOR );
176 TMR4_ENBL &= ~7;
177 TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR;
178 TMR4_CSCTRL0 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN;
179 TMR4_CNTR0 = 0;
180 TMR4_LOAD0 = 0;
181 TMR4_COMP10 = comp1load[0];
182 TMR4_CMPLD10 = comp1load[0];
183 TMR4_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(3);
184 TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
185 TMR4_CNTR1 = 0;
186 TMR4_LOAD1 = 0;
187 TMR4_COMP11 = comp1load[1]; // T0H
188 TMR4_CMPLD11 = comp1load[1];
189 TMR4_CTRL1 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3);
190 TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
191 TMR4_CNTR2 = 0;
192 TMR4_LOAD2 = 0;
193 TMR4_COMP12 = comp1load[2]; // T1H
194 TMR4_CMPLD12 = comp1load[2];
195 TMR4_CTRL2 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3);
196
197 // route the timer outputs through XBAR to edge trigger DMA request: only 4 mappings avail.
198 CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON);
199 xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_DMA_CH_MUX_REQ30);
200 xbar_connect(XBARA1_IN_QTIMER4_TIMER1, XBARA1_OUT_DMA_CH_MUX_REQ31);
201 xbar_connect(XBARA1_IN_QTIMER4_TIMER2, XBARA1_OUT_DMA_CH_MUX_REQ94);
202 XBARA1_CTRL0 = XBARA_CTRL_STS1 | XBARA_CTRL_EDGE1(3) | XBARA_CTRL_DEN1 |
203 XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0;
204 XBARA1_CTRL1 = XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0;
205
206 // configure DMA channels
207 dma1.begin();
208 dma1.TCD->SADDR = bitmask; // source 4*32b GPIO pin mask
209 dma1.TCD->SOFF = 8; // bytes offset added to SADDR after each transfer
210 // SMOD(4) low bits of SADDR to update with adds of SOFF
211 // SSIZE(3) code for 64 bit transfer size DSIZE(2) code for 32 bit transfer size
212 dma1.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2);
213 dma1.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | // Dest minor loop offsetting enable
214 DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
215 DMA_TCD_NBYTES_MLOFFYES_NBYTES(16); // #bytes to tansfer, offsetting enabled
216 dma1.TCD->SLAST = 0; // add to SADDR after xfer
217 dma1.TCD->DADDR = &GPIO1_DR_SET;
218 dma1.TCD->DOFF = 16384; //&GPIO1_DR_SET + DOFF = next &GPIO2_DR_SET
219 dma1.TCD->CITER_ELINKNO = numbytes * 8; // CITER outer loop count (linking disabled) = # LED bits to write
220 dma1.TCD->DLASTSGA = -65536; // add to DADDR after xfer
221 dma1.TCD->BITER_ELINKNO = numbytes * 8; // Beginning CITER (not decremented by transfer)
222 dma1.TCD->CSR = DMA_TCD_CSR_DREQ; // channel ERQ field cleared when minor loop completed
223 dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_0); // only 4 XBAR1 triggers (DMA MUX mapping)
224
225 dma2next.TCD->SADDR = bitdata; //uint32_t bitdata[BYTES_PER_DMA*64]
226 dma2next.TCD->SOFF = 8;
227 dma2next.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_DSIZE(2);
228 dma2next.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE |
229 DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
230 DMA_TCD_NBYTES_MLOFFYES_NBYTES(16);
231 dma2next.TCD->SLAST = 0;
232 dma2next.TCD->DADDR = &GPIO1_DR_CLEAR;
233 dma2next.TCD->DOFF = 16384;
234 dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
235 dma2next.TCD->DLASTSGA = (int32_t)(dma2next.TCD);
236 dma2next.TCD->BITER_ELINKNO = BYTES_PER_DMA * 8;
237 dma2next.TCD->CSR = 0;
238
239 dma2.begin();
240 dma2 = dma2next; // copies TCD
241 dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_1);
242 dma2.attachInterrupt(isr);
243
244 dma3.begin();
245 dma3.TCD->SADDR = bitmask;
246 dma3.TCD->SOFF = 8;
247 dma3.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2);
248 dma3.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE |
249 DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
250 DMA_TCD_NBYTES_MLOFFYES_NBYTES(16);
251 dma3.TCD->SLAST = 0;
252 dma3.TCD->DADDR = &GPIO1_DR_CLEAR;
253 dma3.TCD->DOFF = 16384;
254 dma3.TCD->CITER_ELINKNO = numbytes * 8;
255 dma3.TCD->DLASTSGA = -65536;
256 dma3.TCD->BITER_ELINKNO = numbytes * 8;
257 dma3.TCD->CSR = DMA_TCD_CSR_DREQ | DMA_TCD_CSR_DONE;
258 dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_2);
259} // begin()
260
261
262//*dest = *bitdata + pin offset
263//*pixels = pin's block in frameBuffer
264//mask = pin's bit position in GPIOR
265//set a pin's mask32 for each color bit=0 at every 4*words32 in bitdata+offset
266void fillbits(uint32_t *dest, const uint8_t *pixels, int n, uint32_t mask) {
267 do {
268 uint8_t pix = *pixels++;
269 if (!(pix & 0x80)) *dest |= mask;
270 dest += 4;
271 if (!(pix & 0x40)) *dest |= mask;
272 dest += 4;
273 if (!(pix & 0x20)) *dest |= mask;
274 dest += 4;
275 if (!(pix & 0x10)) *dest |= mask;
276 dest += 4;
277 if (!(pix & 0x08)) *dest |= mask;
278 dest += 4;
279 if (!(pix & 0x04)) *dest |= mask;
280 dest += 4;
281 if (!(pix & 0x02)) *dest |= mask;
282 dest += 4;
283 if (!(pix & 0x01)) *dest |= mask;
284 dest += 4;
285 } while (--n > 0);
286}
287
288
289void ObjectFLED::genFrameBuffer(uint32_t serp) {
290 uint32_t j = 0;
291 int jChange = -3;
292 if (serp == 0) { // use faster loops if no serp
293 switch (params & 0x3F) {
294 case CORDER_RGBW: // R,G,B = R,G,B - MIN(R,G,B); W = MIN(R,G,B)
295 for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
296 uint8_t minRGB = MIN(*((uint8_t*)drawBuffer + j) * rLevel / 65025, \
297 *((uint8_t*)drawBuffer + j + 1) * rLevel / 65025);
298 minRGB = MIN(minRGB, *((uint8_t*)drawBuffer + j + 2) * rLevel / 65025);
299 *(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025 - minRGB;
300 *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
301 *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
302 *(frameBuffer + i + 3) = minRGB;
303 j += 3;
304 } //for(leds in drawbuffer)
305 break;
306 case CORDER_BRG:
307 for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
308 *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
309 *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
310 *(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
311 j += 3;
312 } //for(leds in drawbuffer)
313 break;
314 case CORDER_GRB:
315 for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
316 *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
317 *(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
318 *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
319 j += 3;
320 } //for(leds in drawbuffer)
321 break;
322 case CORDER_RGB:
323 default:
324 for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
325 *(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
326 *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
327 *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
328 j += 3;
329 } //for(leds in drawbuffer)
330 } // switch()
331 } else { //serpentine
332 switch (params & 0x3F) {
333 case CORDER_RGBW: // R,G,B = R,G,B - MIN(R,G,B); W = MIN(R,G,B)
334 for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
335 uint8_t minRGB = MIN(*((uint8_t*)drawBuffer + j) * rLevel / 65025, \
336 * ((uint8_t*)drawBuffer + j + 1) * rLevel / 65025);
337 minRGB = MIN(minRGB, *((uint8_t*)drawBuffer + j + 2) * rLevel / 65025);
338 if (i % (serp * 4) == 0) {
339 if (jChange < 0) { j = i / 4 * 3; jChange = 3; }
340 else { j = (i / 4 + serp - 1) * 3; jChange = -3; }
341 }
342 *(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025 - minRGB;
343 *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
344 *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
345 *(frameBuffer + i + 3) = minRGB;
346 j += jChange;
347 } //for(leds in drawbuffer)
348 break;
349 case CORDER_BRG:
350 for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
351 if (i % (serp * 3) == 0) {
352 if (jChange < 0) { j = i; jChange = 3; }
353 else { j = i + (serp - 1) * 3; jChange = -3; }
354 }
355 *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
356 *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
357 *(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
358 j += jChange;
359 } //for(leds in drawbuffer)
360 break;
361 case CORDER_GRB:
362 for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
363 if (i % (serp * 3) == 0) {
364 if (jChange < 0) { j = i; jChange = 3; }
365 else { j = i + (serp - 1) * 3; jChange = -3; }
366 }
367 *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
368 *(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
369 *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
370 j += jChange;
371 } //for(leds in drawbuffer)
372 break;
373 case CORDER_RGB:
374 default:
375 for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
376 if (i % (serp * 3) == 0) {
377 if (jChange < 0) { j = i; jChange = 3; }
378 else { j = i + (serp - 1) * 3; jChange = -3; }
379 }
380 *(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
381 *(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
382 *(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
383 j += jChange;
384 } //for(leds in drawbuffer)
385 } // switch()
386 } // else serpentine
387} //genFrameBuffer()
388
389
390// pre-show prior transfer wait, copies drawBuffer -> frameBuffer
391// resets timers, clears pending DMA reqs
392// fills bitdata[BYTES_PER_DMA * 64 * 4 bytes] from frameBuffer with 4-block bitmasks for 0's in led data
393// 4 word32s for each bit in (led data)/pin = 16 * 8 = 96 bitdata bytes for each LED byte: 288 bytes / LED
394// launches DMA with IRQ activation to reload bitdata from frameBuffer
395void ObjectFLED::show(void) {
396 while (!dma3.complete()); // wait for dma to complete before reset/re-use
397
398 //Restore context if needed
399 if (frameBuffer != frameBufferLocal) {
400 numpins = numpinsLocal;
401 frameBuffer = frameBufferLocal;
402 numbytes = numbytesLocal;
403 memcpy(bitmask, bitmaskLocal, 16);
404 memcpy(pin_bitnum, pin_bitnumLocal, numpins);
405 memcpy(pin_offset, pin_offsetLocal, numpins);
406 arm_dcache_flush_delete(bitmask, sizeof(bitmask)); //can't DMA from cached memory
407 // Restore 3 timers to create waveform timing events
408 TMR4_COMP10 = comp1load[0];
409 TMR4_CMPLD10 = comp1load[0];
410 TMR4_COMP11 = comp1load[1]; // T0H
411 TMR4_CMPLD11 = comp1load[1];
412 TMR4_COMP12 = comp1load[2]; // T1H
413 TMR4_CMPLD12 = comp1load[2];
414 //restore DMA loop control
415 dma1.TCD->CITER_ELINKNO = numbytes * 8; // CITER outer loop count (linking disabled) = # LED bits to write
416 dma1.TCD->BITER_ELINKNO = numbytes * 8; // Beginning CITER (not decremented by transfer)
417 dma3.TCD->CITER_ELINKNO = numbytes * 8;
418 dma3.TCD->BITER_ELINKNO = numbytes * 8;
419 } //done restoring context
420
421 genFrameBuffer(serpNumber);
422
423 // disable timers
424 uint16_t enable = TMR4_ENBL;
425 TMR4_ENBL = enable & ~7;
426
427 // force all timer outputs to logic low
428 TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR;
429 TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
430 TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
431
432 // clear any prior pending DMA requests
433 XBARA1_CTRL0 |= XBARA_CTRL_STS1 | XBARA_CTRL_STS0;
434 XBARA1_CTRL1 |= XBARA_CTRL_STS0;
435
436 // fill the DMA transmit buffer
437 memset(bitdata, 0, sizeof(bitdata)); //BYTES_PER_DMA * 64 words32
438 uint32_t count = numbytes; //bytes per strip
439 if (count > BYTES_PER_DMA*2) count = BYTES_PER_DMA*2;
440 framebuffer_index = count; //ptr to framebuffer last byte output
441
442 //Sets each pin mask in bitdata32[BYTES_PER_DMA*64] for every 0 bit of pin's frameBuffer block bytes
443 for (uint32_t i=0; i < numpins; i++) { //for each pin
444 fillbits(bitdata + pin_offset[i], (uint8_t *)frameBuffer + i*numbytes,
445 count, 1<<pin_bitnum[i]);
446 }
447 arm_dcache_flush_delete(bitdata, count * 128); // don't need bitdata in cache for DMA
448
449 // set up DMA transfers
450 if (numbytes <= BYTES_PER_DMA*2) {
451 dma2.TCD->SADDR = bitdata;
452 dma2.TCD->DADDR = &GPIO1_DR_CLEAR;
453 dma2.TCD->CITER_ELINKNO = count * 8;
454 dma2.TCD->CSR = DMA_TCD_CSR_DREQ;
455 } else {
456 dma2.TCD->SADDR = bitdata;
457 dma2.TCD->DADDR = &GPIO1_DR_CLEAR;
458 dma2.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
459 dma2.TCD->CSR = 0;
460 dma2.TCD->CSR = DMA_TCD_CSR_INTMAJOR | DMA_TCD_CSR_ESG;
461 dma2next.TCD->SADDR = bitdata + BYTES_PER_DMA*32;
462 dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
463 if (numbytes <= BYTES_PER_DMA*3) {
464 dma2next.TCD->CSR = DMA_TCD_CSR_ESG;
465 } else {
466 dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR;
467 }
468 dma_first = true;
469 }
470 dma3.clearComplete();
471 dma1.enable();
472 dma2.enable();
473 dma3.enable();
474
475 // initialize timers
476 TMR4_CNTR0 = 0;
477 TMR4_CNTR1 = comp1load[0] + 1;
478 TMR4_CNTR2 = comp1load[0] + 1;
479
480 // wait for last LED reset to finish
481 while (micros() - update_begin_micros < numbytes * 8 * TH_TL / OC_FACTOR / 1000 + LATCH_DELAY);
482
483 // start everything running!
484 TMR4_ENBL = enable | 7;
485 update_begin_micros = micros();
486} // show()
487
488
489//INPUT: dma2, dma2next, bitdata, framebuffer_inedex, numpins, numbytes, pin_offset[], pin_bitnum[]
490//Reads next block of framebuffer -> fillbits() -> bitdata
491//Checks for last block to transfer, next to last, or not to update dma2next major loop
492void ObjectFLED::isr(void)
493{
494 // first ack the interrupt
495 dma2.clearInterrupt();
496
497 // fill (up to) half the transmit buffer with new fillbits(frameBuffer data)
498 //digitalWriteFast(12, HIGH);
499 uint32_t *dest;
500 if (dma_first) {
501 dma_first = false;
502 dest = bitdata;
503 } else {
504 dma_first = true;
505 dest = bitdata + BYTES_PER_DMA*32;
506 }
507 memset(dest, 0, sizeof(bitdata)/2);
508 uint32_t index = framebuffer_index;
509 uint32_t count = numbytes - framebuffer_index;
510 if (count > BYTES_PER_DMA) count = BYTES_PER_DMA;
511 framebuffer_index = index + count;
512 for (int i=0; i < numpins; i++) {
513 fillbits(dest + pin_offset[i], (uint8_t *)frameBuffer + index + i*numbytes,
514 count, 1<<pin_bitnum[i]);
515 }
516 arm_dcache_flush_delete(dest, count * 128);
517 //digitalWriteFast(12, LOW);
518
519 // queue it for the next DMA transfer
520 dma2next.TCD->SADDR = dest;
521 dma2next.TCD->CITER_ELINKNO = count * 8;
522 uint32_t remain = numbytes - (index + count);
523 if (remain == 0) {
524 dma2next.TCD->CSR = DMA_TCD_CSR_DREQ;
525 } else if (remain <= BYTES_PER_DMA) {
526 dma2next.TCD->CSR = DMA_TCD_CSR_ESG;
527 } else {
528 dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR;
529 }
530} // isr()
531
532
533int ObjectFLED::busy(void)
534{
535 if (micros() - update_begin_micros < numbytes * TH_TL / OC_FACTOR / 1000 * 8 + LATCH_DELAY) {
536 return 1;
537 }
538 return 0;
539}
540
541
542void ObjectFLED::setBrightness(uint8_t brightLevel) {
543 brightness = brightLevel;
544 rLevel = (brightness + 1) * (colorBalance >> 16);
545 gLevel = (brightness + 1) * ((colorBalance >> 8) & 0xFF);
546 bLevel = (brightness + 1) * (colorBalance & 0xFF);
547 }
548
549
550void ObjectFLED::setBalance(uint32_t balMask) {
551 colorBalance = balMask & 0xFFFFFF;
552 rLevel = (brightness + 1) * (colorBalance >> 16);
553 gLevel = (brightness + 1) * ((colorBalance >> 8) & 0xFF);
554 bLevel = (brightness + 1) * (colorBalance & 0xFF);
555}
556
557
558//Fades CRGB array towards the background color by amount.
559void fadeToColorBy(void* leds, uint16_t count, uint32_t color, uint8_t fadeAmt) {
560 for (uint32_t x = 0; x < count * 3; x += 3) {
561 //fade red
562 *((uint8_t*)leds + x) = (( *((uint8_t*)leds + x) * (1 + (255 - fadeAmt))) >> 8) + \
563 (( ((color >> 16) & 0xFF) * (1 + fadeAmt)) >> 8);
564 //fade green
565 *((uint8_t*)leds + x + 1) = (( *((uint8_t*)leds + x + 1) * (1 + (255 - fadeAmt))) >> 8) + \
566 (( ((color >> 8) & 0xFF) * (1 + fadeAmt)) >> 8);
567 //fade blue
568 *((uint8_t*)leds + x + 2) = (( *((uint8_t*)leds + x + 2) * (1 + (255 - fadeAmt))) >> 8) + \
569 (( (color & 0xFF) * (1 + fadeAmt)) >> 8);
570 }
571} //fadeToColorBy()
572
573
574// Safely draws box even if partially offscreen on 2D CRGB array
575void drawSquare(void* leds, uint16_t planeY, uint16_t planeX, int yCorner, int xCorner, uint32_t size, uint32_t color) {
576 if (size != 0) { size--; }
577 else { return; }
578 for (int x = xCorner; x <= xCorner + (int)size; x++) {
579 // if validX { if validY+S {draw Y+S,X}; if validY {draw Y, X} }
580 if ((x >= 0) && (x < planeX)) { //valid X
581 if ((yCorner >= 0) && (yCorner < planeY)) {
582 *((uint8_t*)leds + (yCorner * planeX + x) * 3) = ((color >> 16) & 0xFF);
583 *((uint8_t*)leds + (yCorner * planeX + x) * 3 + 1) = ((color >> 8) & 0xFF);
584 *((uint8_t*)leds + (yCorner * planeX + x) * 3 + 2) = (color & 0xFF);
585 }
586 if ((yCorner + size >= 0) && (yCorner + size < planeY)) {
587 *((uint8_t*)leds + ((yCorner + size) * planeX + x) * 3) = ((color >> 16) & 0xFF);
588 *((uint8_t*)leds + ((yCorner + size) * planeX + x) * 3 + 1) = ((color >> 8) & 0xFF);
589 *((uint8_t*)leds + ((yCorner + size) * planeX + x) * 3 + 2) = (color & 0xFF);
590 }
591 } //if valid x
592 } //for x
593 for (int y = yCorner; y <= yCorner + (int)size; y++) {
594 if ((y >= 0) && (y < planeY)) { //valid y
595 if ((xCorner >= 0) && (xCorner < planeX)) {
596 *((uint8_t*)leds + (xCorner + y * planeX) * 3) = ((color >> 16) & 0xFF);
597 *((uint8_t*)leds + (xCorner + y * planeX) * 3 + 1) = ((color >> 8) & 0xFF);
598 *((uint8_t*)leds + (xCorner + y * planeX) * 3 + 2) = (color & 0xFF);
599 }
600 if ((xCorner + size >= 0) && (xCorner + size < planeX)) {
601 *((uint8_t*)leds + (xCorner + size + y * planeX) * 3) = ((color >> 16) & 0xFF);
602 *((uint8_t*)leds + (xCorner + size + y * planeX) * 3 + 1) = ((color >> 8) & 0xFF);
603 *((uint8_t*)leds + (xCorner + size + y * planeX) * 3 + 2) = (color & 0xFF);
604 }
605 } //if valid y
606 } //for y
607} // drawSquare()
608
609#endif // __IMXRT1062__