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