diff --git a/ObjectFLED.h b/ObjectFLED.h index 6c2bdc7..652572a 100644 --- a/ObjectFLED.h +++ b/ObjectFLED.h @@ -94,21 +94,24 @@ public: ObjectFLED(uint16_t numLEDs, void* drawBuf, uint8_t config, uint8_t numPins, const uint8_t* pinList, \ uint8_t serpentine = 0); - ~ObjectFLED() { delete frameBuffer; } + ~ObjectFLED() { + // Wait for prior xmission to end, don't need to wait for latch time before deleting buffer + while (micros() - update_begin_micros < numbytes * 8 * TH_TL / OC_FACTOR / 1000 + 5); + delete frameBuffer; + } - //begin() - Use defalut LED timing: 1.0 OC Factor, 1250 nS CLK (=800 KHz), 417 nS T0H, 834 nS T1H, 70 uS LED Latch Delay. + //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. void begin(void); - //begin(LED_Overclock_Factor) - divides default 1250 nS LED CLK (=800 KHz), 417 nS T0H, 834 nS T1H. - void begin(float); + //begin(LED_Latch_Delay_uS) - sets the LED Latch Delay. + void begin(uint16_t); //begin(LED_Overclock_Factor, LED_Latch_Delay_uS) - divides default 1250 nS LED CLK (=800 KHz), - // 417 nS T0H, 834 nS T1H; and sets the LED Latch Delay. - void begin(float, uint16_t); + // 300 nS T0H, 750 nS T1H; and optionally sets the LED Latch Delay. + void begin(double, uint16_t = 300); - //begin(LED_Overclock_Factor, LED_CLK_nS, LED_T0H_nS, LED_T1H_nS, LED_Latch_Delay_uS) - - //specifies full LED timing. Values given for CLK, T0H, T1H are divided by OC Factor. - void begin(float, uint16_t, uint16_t, uint16_t, uint16_t); + //begin(LED_CLK_nS, LED_T0H_nS, LED_T1H_nS, LED_Latch_Delay_uS) - specifies full LED waveform timing. + void begin(uint16_t, uint16_t, uint16_t, uint16_t = 300); void show(void); @@ -151,11 +154,11 @@ private: uint8_t pinlist[NUM_DIGITAL_PINS]; uint16_t comp1load[3]; uint8_t serpNumber; - float OC_FACTOR = 1.0; //used to reduce period of LED output - uint16_t TH_TL = 1250; //nS- period of LED output - uint16_t T0H = TH_TL / 3; //nS- duration of T0H - uint16_t T1H = TH_TL * 2 / 3; //nS- duration of T1H - uint16_t LATCH_DELAY = 75; //uS time to hold output low for LED latch. + float OC_FACTOR = 1.0; //used to reduce period of LED output + uint16_t TH_TL = 1250; //nS- period of LED output + uint16_t T0H = 300; //nS- duration of T0H + uint16_t T1H = 750; //nS- duration of T1H + uint16_t LATCH_DELAY = 300; //uS time to hold output low for LED latch. //for show context switch uint32_t bitmaskLocal[4]; diff --git a/OjectFLED.cpp b/OjectFLED.cpp index 799b796..58fc28f 100644 --- a/OjectFLED.cpp +++ b/OjectFLED.cpp @@ -53,6 +53,8 @@ GOIO9List = { 2, 3, 4, 5, 29, 33, 48, 49, 50, 51, 52, 53, 54 } //6 top, 7 botto * FrameBuffer no longer passed in, constructor now creates buffer; destructor added * Added support for per-object setting of OC factor, TH+TL, T0H, T1H, and LATCH_DELAY in begin function * Set DSE=3, SPEED=0, SRE=0 on output pins per experiment & PJRC forum guidance +* New default values for TH_TL, T0H, T1H, LATCH_DELAY to work with Audio lib and more LED types +* Added wait for prior xmission to complete in destructor */ #ifndef __IMXRT1062__ @@ -114,21 +116,20 @@ static volatile uint32_t *standard_gpio_addr(volatile uint32_t *fastgpio) { } -void ObjectFLED::begin(float OCF) { - OC_FACTOR = OCF; - begin(); -} - - -void ObjectFLED::begin(float OCF, uint16_t latchDelay) { - OC_FACTOR = OCF; +void ObjectFLED::begin(uint16_t latchDelay) { LATCH_DELAY = latchDelay; begin(); } -void ObjectFLED::begin(float OCF, uint16_t period, uint16_t t0h, uint16_t t1h, uint16_t latchDelay) { - OC_FACTOR = OCF; +void ObjectFLED::begin(double OCF, uint16_t latchDelay) { + OC_FACTOR = (float)OCF; + LATCH_DELAY = latchDelay; + begin(); +} + + +void ObjectFLED::begin(uint16_t period, uint16_t t0h, uint16_t t1h, uint16_t latchDelay) { TH_TL = period; T0H = t0h; T1H = t1h; @@ -173,7 +174,7 @@ void ObjectFLED::begin(void) { // Set up 3 timers to create waveform timing events comp1load[0] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)TH_TL / OC_FACTOR ); comp1load[1] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T0H / OC_FACTOR ); - comp1load[2] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T1H / OC_FACTOR ); + comp1load[2] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T1H / (1.0 + ((OC_FACTOR - 1.0)/3)) ); TMR4_ENBL &= ~7; TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR; TMR4_CSCTRL0 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN; diff --git a/README.md b/README.md index f45dc65..3816884 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,12 @@ overclock factor, 256 LED per pin x 16 pins = 4096 LEDs total at 204 back-to-bac ObjectFLED works with FastLED arrays of CRGB, or other drawing buffer in RGB 3-byte format. ### Glossary: + * "LED object" or "display object" refers to the ObjectFLED display object(s) in your code. * "LED device" refers to physical LEDs: strings, planes, cubes, etc. -* "Segment" refers to physical LEDs connected to a single pin. This can be a group of LED devices -(physically daisy-chained) or a subset of LED devices in a larger LED device (rows of a plane, -planes of a cube, a single string of house lights, an orbit of an electron, etc.) +* "Segment" refers to physical LEDs connected to a single pin. This can be a string or a group +of LED devices (physically daisy-chained), or a subset of LED devices in a larger LED device (rows of a plane, +planes of a cube, etc. ## SUPPORTED @@ -29,14 +30,13 @@ planes of a cube, a single string of house lights, an orbit of an electron, etc. * You can independently configure, control and display multiple LED devices connected to your Teensy 4.x, even if the devices use different LED types with different specs. Combine LED devices into a single display object, or define separate objects for segments of each device, or just plain -one display object for each device. It is also possible to define 2 -display objects to display the same drawing buffer on 2 different LED devices. +one display object for each device. It is also possible to define 2 display objects to display the +same drawing buffer on 2 different LED devices. -* Large LED devices can be driven with parallel output to separate segments of the device. Physical -parallelism allows for multi-fold increase in refresh rate. ObjectFLED automatically breaks your -drawing buffer into segments and writes to the pins simultaneously in the specified order. Simply -define an ordered pin list for your object, in the same order they are connected physically to the -segments in your LED device. ObjectFLED performs segmentation "under the covers". +* Large LED devices can be driven with parallel output to separate segments of the device, allowing +for multi-fold increase in refresh rate. ObjectFLED breaks your drawing buffer into segments and +writes them to the pins simultaneously in the specified order. Simply define an ordered pin list +for your object, in the same order they are connected physically to the segments in your LED device. * ObjectFLED works with arrays of CRGB for full compatibility with the rich suite of LED graphics functions of FastLED. But you can use any memory object for drawing buffer, so long as it is 3- @@ -44,7 +44,10 @@ bytes per LED in RGB order. * You can tweak the shape of the LED data waveform generated by this driver. By calling begin() with full pulse timing specs, you can tweak the waveform to achieve the highest possible overclock -(and highest back-to-back refresh rate). +(and highest back-to-back refresh rate). ObjectFLED sends a latch pulse at the end of every frame, +and defaults to a 300 uS latch period. Some WS2812s specify only 50 uS latch period. To make a +noticable increase in refresh rate, try 75 uS latch if yours are rated for 50 (I had to use 72 on a +50 uS latch LED). * Show() function has built-in handling for serpentine, color order, brightness, and color balance for each object. Like in FastLED, these are applied to the frame buffer, not your drawing buffer. @@ -68,15 +71,12 @@ level). When taking advantage of parallel inputs into an LED device, each pin is assumed to drive the same number of LEDs (or rows or planes). When you define your ObjectFLED object, provide an ordered pin -list which matches the order in which the pins connect to the device. ObjectFLED will output -segments from your display buffer to those pins in the order you specify, and simultaneously. +list which matches the order in which the pins connect to the device. Individual LEDs come with various formats for RGB color order, data signal clock frequency, and pulse timing specs. ObjectFLED defaults will work for popular RGB-order LEDs with 800KHz clock and -75uS latch delay (Note that for tested WS2812's, which specify 50uS latch, I had to use 72, perhaps -because of added CPU overhead). Use the begin( params ) function on an ObjectFLED object in order -to use timing specs from your LED's datasheet. Specify the color order when you instantiate -ObjectFLED. +300uS latch delay. Use the begin(params) function on an ObjectFLED object in order to use timing specs +from your LED's datasheet. Specify the color order when you instantiate ObjectFLED. ## USAGE @@ -106,24 +106,25 @@ where Z is plane, Y is row, X is pixel in row. Use variable names which partially match for creating a drawing array and it's matching display object. Ex.- - CRGB grid4[Y][X]; ObjectFLED dspGrid4( Y*X, grid4, etc. ); + CRGB grid4[Y][X]; + ObjectFLED dspGrid4( Y*X, grid4, etc. ); ### begin() FUNCTION Called once in setup for the display object -// Default timing 800KHz clock, 75uS latch delay +// Default timing 800KHz clock, 300 nS T0H, 750 nS T1H, and 300 uS latch delay begin(void); -// Overclock default timing by the given factor -begin(float OCF); +// Override the default latch delay +begin(uint16_t latchDelay); -// Overclock default timing and overrde default latch delay -begin(float OCF, uint16_t latchDelay); +// Overclock default timing and optionally overrde default latch delay +begin(double OCF, uint16_t latchDelay = 300); -// Fully specify output waveform timing. NOTE: Given period, t0h, t1h are divided by given OCF. -begin(float OCF, uint16_t period, uint16_t t0h, uint16_t t1h, uint16_t latchDelay); +// Fully specify output waveform timing TH_TL (clk period), T0H, T1H, and optionally Latch Delay_ +begin(uint16_t period, uint16_t t0h, uint16_t t1h, uint16_t latchDelay = 300); - OCF = Overclocking factor multiples the clock rate (by dividing the pulse width values below) - period = nS time for duration of a full LED data pulse (from LED datasheet) NOTE: For 800KHz clock, @@ -131,14 +132,11 @@ period = 1250nS. This is the default setting. - t0h = nS time for duration of high portion of pulse for LED data 0 (from LED datasheet) - t1h = nS time for duration of high portion of pulse for LED data 1 (from LED datasheet) - latchDelay = uS time to hold data low for LEDs to latch color data to DACs (from LED datasheet) -NOTE: In test I had to use 72uS latch delay for LEDs with a spec of 50uS, in order to stop - a severe display glitch. I've read at least one LED model specifies 300uS latch. If you - get display glitches on a new LED type, latch time should be the first thing to check. **Example:** dispCube.begin(1.5, 72); //overclocks by factor 1.5 (1200KHz) and sets 72uS latch delay - dispVid.begin(1.68, 1250, 420, 840, 72); //420nS, 840nS are 1/3, 2/3 of period + dispVid.begin(1250, 420, 840, 72); //420nS, 840nS are longer than typical ### show() FUNCTION @@ -166,7 +164,7 @@ drawing buffer. * setBrightness(uint8_t); -* getBrightness() { return uint8_t brightness; } +* uint8_t getBrightness(); ### setBalance(), getBalance() FUNCTIONS @@ -176,7 +174,7 @@ Like brightness, this is applied by show() to frame buffer, not to your drawing * setBalance(uint32_t); -* getBalance() { return uint32_t colorBalance; } +* uint32_t getBalance(); ## ACCESSORY FUNCTIONS diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..e05f947 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,21 @@ +### Release 1.1.0 +* Changed default LED waveform timing to fix conflict with Teensy Audio library and possibly other DMA-enabled apps. +More relaxed default timing allows ObjectFLED to work out-of-the-box with more LED chips as well. +* Eliminated using both overclock factor and pulse timing specs in same .begin() function call. Either specify OC factor, +or pulse timing values, but not both. See mouseover help or OctoFLED.h for the updated .begin() signatures. Only those +using the full form of begin(OC_Factor, THTL, T0H, T1H, Latch_Delay) will need to update their .begin() call. +* Changed how OC factor is applied to waveform timing. Originally, OC factor was applied to equally shrink TH_TL, T0H, +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 +LED chips have a fixed threshold for when a H pulse is a 0 or a 1. This change yeilded slightly better overlockability +in testing with WS2812B chips._ + +### Release 1.0.3 +* Added support for GBR, BGR color formats. + +### Release 1.0.2 +* Set DSE=3 on output pins improved LED overclocking by 7% (boot default DSE=6). + +### Release 1.0.1 +* Updated includes to replace include Arduino.h. +* Started making releases, added INSTALLATION.md file. +* Tweakage to readme. \ No newline at end of file diff --git a/examples/ObjectFLEDExperiment/ObjectFLEDExperiment.ino b/examples/ObjectFLEDExperiment/ObjectFLEDExperiment.ino index b7a687c..b82a040 100644 --- a/examples/ObjectFLEDExperiment/ObjectFLEDExperiment.ino +++ b/examples/ObjectFLEDExperiment/ObjectFLEDExperiment.ino @@ -58,7 +58,7 @@ void setup() { //"electron orbit" around 3 4x4 planes driven by 3 pins //1.68 OC factor with these timing values are max OC for YF923's I found posing as WS2812B's - electron.begin(1.68, 1250, 420, 840, 72); + electron.begin(1.6, 72); electron.setBrightness(brightness); electron.setBalance(0xdae0ff); fill_solid(electronLED[0][0], NUM_LEDS, background); diff --git a/examples/ObjectFLEDTest/ObjectFLEDTest.ino b/examples/ObjectFLEDTest/ObjectFLEDTest.ino index f22b22a..a7597ad 100644 --- a/examples/ObjectFLEDTest/ObjectFLEDTest.ino +++ b/examples/ObjectFLEDTest/ObjectFLEDTest.ino @@ -15,8 +15,6 @@ const int PIX_PER_ROW = 16, NUM_PLANES = 16, NUM_CHANNELS = 16, COLOR_ORDER = RGB, - //LED baud * 3 bits/LED byte = Serial baud; 2.4 MHz serial, 3.6 passed testing - LED_SERIAL_BAUD = 800 * 1.8, //1200, //SerialFLED in kHz STD_OUT_BAUD = 100000; const CRGB background = 0x505000; byte pinList[NUM_CHANNELS] = {1, 8, 14, 17, 24, 29, 20, 0, 15, 16, 18, 19, 21, 22, 23, 25}; @@ -49,7 +47,7 @@ void setup() { // FastLED.setCorrection(0xB0E0FF); fill_solid(blankLeds[0][0], 7*8*8, 0x0); fill_solid(testCube[0][0], NUM_PLANES * NUM_ROWS * PIX_PER_ROW, background); - leds.begin(1.6, 72); //1.6 ocervlock factor, 72uS LED latch delay + leds.begin(); // Use alternate forms of this function to overclock leds.setBrightness(6); leds.setBalance(0xDAE0FF); @@ -82,7 +80,7 @@ void loop() { leds.show(); leds.show(); stopT = micros(); - Serial.printf("LEDs/channel: %d Serial avg/20 time: %d uS %f fps\n", \ + Serial.printf("LEDs/channel: %d ObjectFLED avg/20 time: %d uS %f fps\n", \ PIX_PER_ROW*NUM_ROWS*NUM_PLANES/NUM_CHANNELS, (stopT - startT) / 20, 20000000.0 / (stopT - startT)); while (Serial.read() == -1); @@ -90,7 +88,7 @@ void loop() { startT = micros(); leds.show(); stopT = micros(); - Serial.printf("LEDs/channel: %d Serial 1 frame time: %d uS %f fps\n", \ + Serial.printf("LEDs/channel: %d ObjectFLED 1 frame time: %d uS %f fps\n", \ PIX_PER_ROW*NUM_ROWS*NUM_PLANES/NUM_CHANNELS, (stopT - startT), 1000000.0 / (stopT - startT)); while (Serial.read() == -1); @@ -121,7 +119,7 @@ return; FastLED.show(); FastLED.show(); stopT = micros(); - Serial.printf("LEDs/channel: %d FLED avg/20 time: %d uS %f fps\n", \ + Serial.printf("LEDs/channel: %d FastLED avg/20 time: %d uS %f fps\n", \ PIX_PER_ROW*NUM_ROWS*NUM_PLANES/NUM_CHANNELS, (stopT - startT) / 20, 20000000.0 / (stopT - startT)); //Serial.printf("CPU temp: %.1f C %.1f F\n", tempmonGetTemp(), tempmonGetTemp() * 9.0 / 5.0 + 32); @@ -130,7 +128,7 @@ return; startT = micros(); FastLED.show(); stopT = micros(); - Serial.printf("LEDs/channel: %d FLED 1 frame time: %d uS %f fps\n", \ + Serial.printf("LEDs/channel: %d FastLED 1 frame time: %d uS %f fps\n", \ PIX_PER_ROW*NUM_ROWS*NUM_PLANES/NUM_CHANNELS, (stopT - startT), 1000000.0 / (stopT - startT)); while (Serial.read() == -1);