Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/FastLED/FastLED.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Garcia <danielgarcia@gmail.com>2015-09-23 19:18:28 +0300
committerDaniel Garcia <danielgarcia@gmail.com>2015-09-23 19:18:28 +0300
commit45084481ecf3c16d0deafb2f73fa2f3228a2ac3d (patch)
treea01dd340d9d2bd0bd5db3c3f3b55dfe7d4fda55a
parentd6f662cf742b5cefdaa4296a68c90b3a7471a240 (diff)
parent3cb612d02759c0068c1b0e72e1c4e4d00552eee6 (diff)
Merging 3.1 back to master
-rw-r--r--.gitignore1
-rw-r--r--FastLED.cpp111
-rw-r--r--FastLED.h356
-rw-r--r--FastSPI_LED2.h2
-rw-r--r--PORTING.md29
-rw-r--r--README.md26
-rw-r--r--bitswap.h272
-rw-r--r--block_clockless.h232
-rw-r--r--chipsets.h114
-rw-r--r--clockless.h28
-rw-r--r--clockless2.h241
-rw-r--r--clockless_arm_sam.h217
-rw-r--r--clockless_trinket.h366
-rw-r--r--color.h56
-rw-r--r--colorpalettes.cpp94
-rw-r--r--colorpalettes.h72
-rw-r--r--colorutils.cpp368
-rw-r--r--colorutils.h513
-rw-r--r--controller.h332
-rw-r--r--dmx.h12
-rw-r--r--docs/.Doxyfile.swpbin0 -> 126976 bytes
-rw-r--r--docs/Doxyfile2331
-rw-r--r--docs/mainpage.dox10
-rw-r--r--examples/Blink/Blink.ino7
-rw-r--r--examples/ColorPalette/ColorPalette.ino165
-rw-r--r--examples/Cylon/Cylon.ino32
-rw-r--r--examples/DemoReel100/DemoReel100.ino126
-rw-r--r--examples/Fire2012/Fire2012.ino13
-rw-r--r--examples/Fire2012WithPalette/Fire2012WithPalette.ino11
-rw-r--r--examples/FirstLight/FirstLight.ino12
-rw-r--r--examples/Multiple/OctoWS2811Demo/OctoWS2811Demo.ino37
-rw-r--r--examples/Multiple/ParallelOutputDemo/ParallelOutputDemo.ino47
-rw-r--r--examples/Ports/PJRCSpectrumAnalyzer/PJRCSpectrumAnalyzer.ino136
-rw-r--r--extras/AppleII.s6540
-rw-r--r--extras/FastLED6502.s65633
-rw-r--r--extras/RainbowDemo.bin.zipbin0 -> 848 bytes
-rw-r--r--extras/RainbowDemo.s6589
-rw-r--r--fastled_config.h22
-rw-r--r--fastled_delay.h (renamed from delay.h)104
-rw-r--r--fastled_progmem.h64
-rw-r--r--fastpin.h126
-rw-r--r--fastspi.h99
-rw-r--r--fastspi_bitbang.h22
-rw-r--r--fastspi_nop.h64
-rw-r--r--fastspi_ref.h39
-rw-r--r--fastspi_types.h41
-rw-r--r--hsv2rgb.cpp239
-rw-r--r--hsv2rgb.h41
-rw-r--r--keywords.txt54
-rw-r--r--led_sysdefs.h73
-rw-r--r--lib8tion.cpp23
-rw-r--r--lib8tion.h1517
-rw-r--r--lib8tion/math8.h357
-rw-r--r--lib8tion/random8.h94
-rw-r--r--lib8tion/scale8.h511
-rw-r--r--lib8tion/trig8.h259
-rw-r--r--library.properties9
-rw-r--r--noise.cpp142
-rw-r--r--noise.h127
-rw-r--r--pixeltypes.h136
-rw-r--r--platforms.h28
-rw-r--r--platforms/arm/common/m0clockless.h313
-rw-r--r--platforms/arm/d21/clockless_arm_d21.h89
-rw-r--r--platforms/arm/d21/fastled_arm_d21.h8
-rw-r--r--platforms/arm/d21/fastpin_arm_d21.h95
-rw-r--r--platforms/arm/d21/led_sysdefs_arm_d21.h26
-rw-r--r--platforms/arm/k20/clockless_arm_k20.h (renamed from clockless_arm_k20.h)53
-rw-r--r--platforms/arm/k20/clockless_block_arm_k20.h396
-rw-r--r--platforms/arm/k20/fastled_arm_k20.h14
-rw-r--r--platforms/arm/k20/fastpin_arm_k20.h (renamed from fastpin_arm_k20.h)35
-rw-r--r--platforms/arm/k20/fastspi_arm_k20.h (renamed from fastspi_arm_k20.h)367
-rw-r--r--platforms/arm/k20/led_sysdefs_arm_k20.h46
-rw-r--r--platforms/arm/k20/octows2811_controller.h96
-rw-r--r--platforms/arm/k20/smartmatrix_t3.h (renamed from smartmatrix_t3.h)4
-rw-r--r--platforms/arm/kl26/clockless_arm_kl26.h89
-rw-r--r--platforms/arm/kl26/fastled_arm_kl26.h10
-rw-r--r--platforms/arm/kl26/fastpin_arm_kl26.h88
-rw-r--r--platforms/arm/kl26/fastspi_arm_kl26.h252
-rw-r--r--platforms/arm/kl26/led_sysdefs_arm_kl26.h45
-rw-r--r--platforms/arm/nrf51/clockless_arm_nrf51.h117
-rw-r--r--platforms/arm/nrf51/fastled_arm_nrf51.h11
-rw-r--r--platforms/arm/nrf51/fastpin_arm_nrf51.h119
-rw-r--r--platforms/arm/nrf51/fastspi_arm_nrf51.h146
-rw-r--r--platforms/arm/nrf51/led_sysdefs_arm_nrf51.h44
-rw-r--r--platforms/arm/sam/clockless_arm_sam.h145
-rw-r--r--platforms/arm/sam/clockless_block_arm_sam.h206
-rw-r--r--platforms/arm/sam/fastled_arm_sam.h11
-rw-r--r--platforms/arm/sam/fastpin_arm_sam.h (renamed from fastpin_arm_sam.h)44
-rw-r--r--platforms/arm/sam/fastspi_arm_sam.h (renamed from fastspi_arm_sam.h)3
-rw-r--r--platforms/arm/sam/led_sysdefs_arm_sam.h39
-rw-r--r--platforms/arm/stm32/clockless_arm_stm32.h147
-rw-r--r--platforms/arm/stm32/fastled_arm_stm32.h10
-rw-r--r--platforms/arm/stm32/fastpin_arm_stm32.h105
-rw-r--r--platforms/arm/stm32/led_sysdefs_arm_stm32.h47
-rw-r--r--platforms/avr/clockless_trinket.h514
-rw-r--r--platforms/avr/fastled_avr.h14
-rw-r--r--platforms/avr/fastpin_avr.h (renamed from fastpin_avr.h)181
-rw-r--r--platforms/avr/fastspi_avr.h (renamed from fastspi_avr.h)291
-rw-r--r--platforms/avr/led_sysdefs_avr.h52
-rw-r--r--power_mgt.cpp34
-rw-r--r--power_mgt.h37
-rw-r--r--preview_changes.txt41
-rw-r--r--release_notes.md2
-rw-r--r--wiring.cpp238
104 files changed, 12474 insertions, 3472 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..5ccff1a6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+html/
diff --git a/FastLED.cpp b/FastLED.cpp
index c87e8e31..9de0ce33 100644
--- a/FastLED.cpp
+++ b/FastLED.cpp
@@ -1,18 +1,20 @@
+#define FASTLED_INTERNAL
#include "FastLED.h"
+
#if defined(__SAM3X8E__)
volatile uint32_t fuckit;
#endif
+FASTLED_NAMESPACE_BEGIN
+
void *pSmartMatrix = NULL;
-CFastLED LEDS;
-CFastLED & FastSPI_LED = LEDS;
-CFastLED & FastSPI_LED2 = LEDS;
-CFastLED & FastLED = LEDS;
+CFastLED FastLED;
CLEDController *CLEDController::m_pHead = NULL;
CLEDController *CLEDController::m_pTail = NULL;
+static uint32_t lastshow = 0;
// uint32_t CRGB::Squant = ((uint32_t)((__TIME__[4]-'0') * 28))<<16 | ((__TIME__[6]-'0')*50)<<8 | ((__TIME__[7]-'0')*28);
@@ -31,10 +33,15 @@ CLEDController &CFastLED::addLeds(CLEDController *pLed,
pLed->init();
pLed->setLeds(data + nOffset, nLeds);
+ FastLED.setMaxRefreshRate(pLed->getMaxRefreshRate(),true);
return *pLed;
}
void CFastLED::show(uint8_t scale) {
+ // guard against showing too rapidly
+ while(m_nMinMicros && ((micros()-lastshow) < m_nMinMicros));
+ lastshow = micros();
+
CLEDController *pCur = CLEDController::head();
while(pCur) {
uint8_t d = pCur->getDither();
@@ -69,6 +76,9 @@ CLEDController & CFastLED::operator[](int x) {
}
void CFastLED::showColor(const struct CRGB & color, uint8_t scale) {
+ while(m_nMinMicros && ((micros()-lastshow) < m_nMinMicros));
+ lastshow = micros();
+
CLEDController *pCur = CLEDController::head();
while(pCur) {
uint8_t d = pCur->getDither();
@@ -98,7 +108,11 @@ void CFastLED::clearData() {
void CFastLED::delay(unsigned long ms) {
unsigned long start = millis();
while((millis()-start) < ms) {
+#ifndef FASTLED_ACCURATE_CLOCK
+ // make sure to allow at least one ms to pass to ensure the clock moves
+ // forward
::delay(1);
+#endif
show();
}
}
@@ -127,6 +141,48 @@ void CFastLED::setDither(uint8_t ditherMode) {
}
}
+//
+// template<int m, int n> void transpose8(unsigned char A[8], unsigned char B[8]) {
+// uint32_t x, y, t;
+//
+// // Load the array and pack it into x and y.
+// y = *(unsigned int*)(A);
+// x = *(unsigned int*)(A+4);
+//
+// // x = (A[0]<<24) | (A[m]<<16) | (A[2*m]<<8) | A[3*m];
+// // y = (A[4*m]<<24) | (A[5*m]<<16) | (A[6*m]<<8) | A[7*m];
+//
+ // // pre-transform x
+ // t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
+ // t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
+ //
+ // // pre-transform y
+ // t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
+ // t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
+ //
+ // // final transform
+ // t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
+ // y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
+ // x = t;
+//
+// B[7*n] = y; y >>= 8;
+// B[6*n] = y; y >>= 8;
+// B[5*n] = y; y >>= 8;
+// B[4*n] = y;
+//
+// B[3*n] = x; x >>= 8;
+// B[2*n] = x; x >>= 8;
+// B[n] = x; x >>= 8;
+// B[0] = x;
+// // B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x>>0;
+// // B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y>>0;
+// }
+//
+// void transposeLines(Lines & out, Lines & in) {
+// transpose8<1,2>(in.bytes, out.bytes);
+// transpose8<1,2>(in.bytes + 8, out.bytes + 1);
+// }
+
extern int noise_min;
extern int noise_max;
@@ -142,3 +198,50 @@ void CFastLED::countFPS(int nFrames) {
lastframe = millis();
}
}
+
+void CFastLED::setMaxRefreshRate(uint16_t refresh, bool constrain) {
+ if(constrain) {
+ // if we're constraining, the new value of m_nMinMicros _must_ be higher than previously (because we're only
+ // allowed to slow things down if constraining)
+ if(refresh > 0) {
+ m_nMinMicros = ( (1000000/refresh) > m_nMinMicros) ? (1000000/refresh) : m_nMinMicros;
+ }
+ } else if(refresh > 0) {
+ m_nMinMicros = 1000000 / refresh;
+ } else {
+ m_nMinMicros = 0;
+ }
+}
+
+
+#ifdef NEED_CXX_BITS
+namespace __cxxabiv1
+{
+ extern "C" void __cxa_pure_virtual (void) {}
+ /* guard variables */
+
+ /* The ABI requires a 64-bit type. */
+ __extension__ typedef int __guard __attribute__((mode(__DI__)));
+
+ extern "C" int __cxa_guard_acquire (__guard *);
+ extern "C" void __cxa_guard_release (__guard *);
+ extern "C" void __cxa_guard_abort (__guard *);
+
+ extern "C" int __cxa_guard_acquire (__guard *g)
+ {
+ return !*(char *)(g);
+ }
+
+ extern "C" void __cxa_guard_release (__guard *g)
+ {
+ *(char *)g = 1;
+ }
+
+ extern "C" void __cxa_guard_abort (__guard *)
+ {
+
+ }
+}
+#endif
+
+FASTLED_NAMESPACE_END
diff --git a/FastLED.h b/FastLED.h
index c46e3eae..fd0f63bf 100644
--- a/FastLED.h
+++ b/FastLED.h
@@ -1,14 +1,16 @@
#ifndef __INC_FASTSPI_LED2_H
#define __INC_FASTSPI_LED2_H
-// #define NO_CORRECTION 1
-// #define NO_DITHERING 1
+///@file FastLED.h
+/// central include file for FastLED, defines the CFastLED class/object
#define xstr(s) str(s)
#define str(s) #s
-#define FASTLED_VERSION 3000002
-#warning FastLED version 3.000.002 (Not really a warning, just telling you here.)
+#define FASTLED_VERSION 3001000
+#ifndef FASTLED_INTERNAL
+#warning FastLED version 3.001.000 (Not really a warning, just telling you here.)
+#endif
#ifndef __PROG_TYPES_COMPAT__
#define __PROG_TYPES_COMPAT__
@@ -26,20 +28,34 @@
#include<DMXSerial.h>
#endif
+#include <stdint.h>
+
+#include "fastled_config.h"
+#include "led_sysdefs.h"
+
+#include "bitswap.h"
#include "controller.h"
#include "fastpin.h"
-#include "fastspi.h"
-#include "clockless.h"
+#include "fastspi_types.h"
+#include "./dmx.h"
+
+#include "platforms.h"
+#include "fastled_progmem.h"
+
#include "lib8tion.h"
#include "hsv2rgb.h"
#include "colorutils.h"
#include "colorpalettes.h"
-#include "chipsets.h"
-#include "./dmx.h"
-#include "smartmatrix_t3.h"
+
#include "noise.h"
#include "power_mgt.h"
+#include "fastspi.h"
+#include "chipsets.h"
+
+FASTLED_NAMESPACE_BEGIN
+
+/// definitions for the spi chipset constants
enum ESPIChipsets {
LPD8806,
WS2801,
@@ -51,16 +67,19 @@ enum ESPIChipsets {
};
enum ESM { SMART_MATRIX };
+enum OWS2811 { OCTOWS2811,OCTOWS2811_400 };
-template<uint8_t DATA_PIN> class NEOPIXEL : public WS2811Controller800Khz<DATA_PIN, GRB> {};
+#ifdef FASTLED_HAS_CLOCKLESS
+template<uint8_t DATA_PIN> class NEOPIXEL : public WS2812Controller800Khz<DATA_PIN, GRB> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class TM1829 : public TM1829Controller800Khz<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class TM1809 : public TM1809Controller800Khz<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class TM1804 : public TM1809Controller800Khz<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class TM1803 : public TM1803Controller400Khz<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class UCS1903 : public UCS1903Controller400Khz<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class UCS1903B : public UCS1903BController800Khz<DATA_PIN, RGB_ORDER> {};
-template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2812 : public WS2811Controller800Khz<DATA_PIN, RGB_ORDER> {};
-template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2812B : public WS2811Controller800Khz<DATA_PIN, RGB_ORDER> {};
+template<uint8_t DATA_PIN, EOrder RGB_ORDER> class UCS1904 : public UCS1904Controller800Khz<DATA_PIN, RGB_ORDER> {};
+template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2812 : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {};
+template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2812B : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2811 : public WS2811Controller800Khz<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class APA104 : public WS2811Controller800Khz<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2811_400 : public WS2811Controller400Khz<DATA_PIN, RGB_ORDER> {};
@@ -73,14 +92,29 @@ template<uint8_t DATA_PIN, EOrder RGB_ORDER> class DMXSIMPLE : public DMXSimpleC
#ifdef DmxSerial_h
template<EOrder RGB_ORDER> class DMXSERIAL : public DMXSerialController<RGB_ORDER> {};
#endif
-
-// template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(20)> class LPD8806 : public LPD8806Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED> {};
-// template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(1)> class WS2801 : public WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED> {};
-// template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(15)> class P9813 : public P9813Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED> {};
-// template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(16)> class SM16716 : public SM16716Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED> {};
+#endif
enum EBlockChipsets {
- WS2811_PORTC
+#ifdef PORTA_FIRST_PIN
+ WS2811_PORTA,
+ WS2811_400_PORTA,
+#endif
+#ifdef PORTB_FIRST_PIN
+ WS2811_PORTB,
+ WS2811_400_PORTB,
+#endif
+#ifdef PORTC_FIRST_PIN
+ WS2811_PORTC,
+ WS2811_400_PORTC,
+#endif
+#ifdef PORTD_FIRST_PIN
+ WS2811_PORTD,
+ WS2811_400_PORTD,
+#endif
+#ifdef HAS_PORTDC
+ WS2811_PORTDC,
+ WS2811_400_PORTDC,
+#endif
};
#if defined(LIB8_ATTINY)
@@ -89,15 +123,64 @@ enum EBlockChipsets {
#define NUM_CONTROLLERS 8
#endif
+/// High level controller interface for FastLED. This class manages controllers, global settings and trackings
+/// such as brightness, and refresh rates, and provides access functions for driving led data to controllers
+/// via the show/showColor/clear methods.
+/// @nosubgrouping
class CFastLED {
// int m_nControllers;
- uint8_t m_Scale;
- uint16_t m_nFPS;
+ uint8_t m_Scale; ///< The current global brightness scale setting
+ uint16_t m_nFPS; ///< Tracking for current FPS value
+ uint32_t m_nMinMicros; ///< minimum µs between frames, used for capping frame rates.
public:
CFastLED();
+
+ /// Add a CLEDController instance to the world. Exposed to the public to allow people to implement their own
+ /// CLEDController objects or instances. There are two ways to call this method (as well as the other addLeds)
+ /// variations. The first is with 3 arguments, in which case the arguments are the controller, a pointer to
+ /// led data, and the number of leds used by this controller. The seocond is with 4 arguments, in which case
+ /// the first two arguments are the same, the third argument is an offset into the CRGB data where this controller's
+ /// CRGB data begins, and the fourth argument is the number of leds for this controller object.
+ /// @param pLed - the led controller being added
+ /// @param data - base point to an array of CRGB data structures
+ /// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array
+ /// @param nLedsIfOffset - number of leds (4 argument version)
+ /// @returns a reference to the added controller
static CLEDController &addLeds(CLEDController *pLed, struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0);
+ /// @name Adding SPI based controllers
+ //@{
+ /// Add an SPI based CLEDController instance to the world.
+ /// There are two ways to call this method (as well as the other addLeds)
+ /// variations. The first is with 2 arguments, in which case the arguments are a pointer to
+ /// led data, and the number of leds used by this controller. The seocond is with 3 arguments, in which case
+ /// the first argument is the same, the second argument is an offset into the CRGB data where this controller's
+ /// CRGB data begins, and the third argument is the number of leds for this controller object.
+ ///
+ /// This method also takes a 1 to 5 template parameters for identifying the specific chipset, data and clock pins,
+ /// RGB ordering, and SPI data rate
+ /// @param data - base point to an array of CRGB data structures
+ /// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array
+ /// @param nLedsIfOffset - number of leds (4 argument version)
+ /// @tparam CHIPSET - the chipset type
+ /// @tparam DATA_PIN - the optional data pin for the leds (if omitted, will default to the first hardware SPI MOSI pin)
+ /// @tparam CLOCK_PIN - the optional clock pin for the leds (if omitted, will default to the first hardware SPI clock pin)
+ /// @tparam RGB_ORDER - the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in)
+ /// @tparam SPI_DATA_RATE - the data rate to drive the SPI clock at, defined using DATA_RATE_MHZ or DATA_RATE_KHZ macros
+ /// @returns a reference to the added controller
+ template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER, uint8_t SPI_DATA_RATE > CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+ switch(CHIPSET) {
+ case LPD8806: { static LPD8806Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+ case WS2801: { static WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+ case WS2803: { static WS2803Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+ case SM16716: { static SM16716Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+ case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+ case DOTSTAR:
+ case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+ }
+ }
+
template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN > static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
switch(CHIPSET) {
case LPD8806: { static LPD8806Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
@@ -122,18 +205,6 @@ public:
}
}
- template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER, uint8_t SPI_DATA_RATE > CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
- switch(CHIPSET) {
- case LPD8806: { static LPD8806Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
- case WS2801: { static WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
- case WS2803: { static WS2803Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
- case SM16716: { static SM16716Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
- case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
- case DOTSTAR:
- case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
- }
- }
-
#ifdef SPI_DATA
template<ESPIChipsets CHIPSET> static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
return addLeds<CHIPSET, SPI_DATA, SPI_CLOCK, RGB>(data, nLedsOrOffset, nLedsIfOffset);
@@ -148,13 +219,27 @@ public:
}
#endif
-
- template<template<uint8_t DATA_PIN> class CHIPSET, uint8_t DATA_PIN>
- static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
- static CHIPSET<DATA_PIN> c;
- return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
- }
-
+ //@}
+
+#ifdef FASTLED_HAS_CLOCKLESS
+ /// @name Adding 3-wire led controllers
+ //@{
+ /// Add a clockless (aka 3wire, also DMX) based CLEDController instance to the world.
+ /// There are two ways to call this method (as well as the other addLeds)
+ /// variations. The first is with 2 arguments, in which case the arguments are a pointer to
+ /// led data, and the number of leds used by this controller. The seocond is with 3 arguments, in which case
+ /// the first argument is the same, the second argument is an offset into the CRGB data where this controller's
+ /// CRGB data begins, and the third argument is the number of leds for this controller object.
+ ///
+ /// This method also takes a 2 to 3 template parameters for identifying the specific chipset, data pin, and rgb ordering
+ /// RGB ordering, and SPI data rate
+ /// @param data - base point to an array of CRGB data structures
+ /// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array
+ /// @param nLedsIfOffset - number of leds (4 argument version)
+ /// @tparam CHIPSET - the chipset type (required)
+ /// @tparam DATA_PIN - the optional data pin for the leds (required)
+ /// @tparam RGB_ORDER - the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in)
+ /// @returns a reference to the added controller
template<template<uint8_t DATA_PIN, EOrder RGB_ORDER> class CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
static CHIPSET<DATA_PIN, RGB_ORDER> c;
@@ -167,18 +252,72 @@ public:
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
}
- template<template<EOrder RGB_ORDER> class CHIPSET>
+ template<template<uint8_t DATA_PIN> class CHIPSET, uint8_t DATA_PIN>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
- static CHIPSET<RGB> c;
+ static CHIPSET<DATA_PIN> c;
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
}
+ #ifdef FASTSPI_USE_DMX_SIMPLE
+ template<EClocklessChipsets CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER=RGB>
+ static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
+ {
+ switch(CHIPSET) {
+ case DMX: { static DMXController<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+ }
+ }
+ #endif
+ //@}
+#endif
+
+ /// @name Adding 3rd party library controllers
+ //@{
+ /// Add a 3rd party library based CLEDController instance to the world.
+ /// There are two ways to call this method (as well as the other addLeds)
+ /// variations. The first is with 2 arguments, in which case the arguments are a pointer to
+ /// led data, and the number of leds used by this controller. The seocond is with 3 arguments, in which case
+ /// the first argument is the same, the second argument is an offset into the CRGB data where this controller's
+ /// CRGB data begins, and the third argument is the number of leds for this controller object. This class includes the SmartMatrix
+ /// and OctoWS2811 based controllers
+ ///
+ /// This method also takes a 1 to 2 template parameters for identifying the specific chipset and rgb ordering
+ /// RGB ordering, and SPI data rate
+ /// @param data - base point to an array of CRGB data structures
+ /// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array
+ /// @param nLedsIfOffset - number of leds (4 argument version)
+ /// @tparam CHIPSET - the chipset type (required)
+ /// @tparam RGB_ORDER - the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in)
+ /// @returns a reference to the added controller
template<template<EOrder RGB_ORDER> class CHIPSET, EOrder RGB_ORDER>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
static CHIPSET<RGB_ORDER> c;
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
}
+ template<template<EOrder RGB_ORDER> class CHIPSET>
+ static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+ static CHIPSET<RGB> c;
+ return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
+ }
+
+#ifdef USE_OCTOWS2811
+ template<OWS2811 CHIPSET, EOrder RGB_ORDER>
+ static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
+ {
+ switch(CHIPSET) {
+ case OCTOWS2811: { static COctoWS2811Controller<RGB_ORDER> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+ case OCTOWS2811_400: { static COctoWS2811Controller<RGB_ORDER,true> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+ }
+ }
+
+ template<OWS2811 CHIPSET>
+ static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
+ {
+ return addLeds<CHIPSET,GRB>(data,nLedsOrOffset,nLedsIfOffset);
+ }
+
+#endif
+
#ifdef SmartMatrix_h
template<ESM CHIPSET>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
@@ -188,63 +327,164 @@ public:
}
}
#endif
-
-#ifdef HAS_BLOCKLESS
- template<EBlockChipsets CHIPSET, int NUM_LANES>
+ //@}
+
+
+#ifdef FASTLED_HAS_BLOCKLESS
+
+ /// @name adding parallel output controllers
+ //@{
+ /// Add a block based CLEDController instance to the world.
+ /// There are two ways to call this method (as well as the other addLeds)
+ /// variations. The first is with 2 arguments, in which case the arguments are a pointer to
+ /// led data, and the number of leds used by this controller. The seocond is with 3 arguments, in which case
+ /// the first argument is the same, the second argument is an offset into the CRGB data where this controller's
+ /// CRGB data begins, and the third argument is the number of leds for this controller object.
+ ///
+ /// This method also takes a 2 to 3 template parameters for identifying the specific chipset and rgb ordering
+ /// RGB ordering, and SPI data rate
+ /// @param data - base point to an array of CRGB data structures
+ /// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array
+ /// @param nLedsIfOffset - number of leds (4 argument version)
+ /// @tparam CHIPSET - the chipset/port type (required)
+ /// @tparam NUM_LANES - how many parallel lanes of output to write
+ /// @tparam RGB_ORDER - the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in)
+ /// @returns a reference to the added controller
+ template<EBlockChipsets CHIPSET, int NUM_LANES, EOrder RGB_ORDER>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
switch(CHIPSET) {
- case WS2811_PORTC: return addLeds(new BlockClocklessController<NUM_LANES, NS(350), NS(350), NS(550)>(), data, nLedsOrOffset, nLedsIfOffset);
+ #ifdef PORTA_FIRST_PIN
+ case WS2811_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
+ case WS2811_400_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
+ #endif
+ #ifdef PORTB_FIRST_PIN
+ case WS2811_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
+ case WS2811_400_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
+ #endif
+ #ifdef PORTC_FIRST_PIN
+ case WS2811_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
+ case WS2811_400_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
+ #endif
+ #ifdef PORTD_FIRST_PIN
+ case WS2811_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
+ case WS2811_400_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
+ #endif
+ #ifdef HAS_PORTDC
+ case WS2811_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<16,NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
+ case WS2811_400_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<16,NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
+ #endif
}
}
+
+ template<EBlockChipsets CHIPSET, int NUM_LANES>
+ static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+ return addLeds<CHIPSET,NUM_LANES,GRB>(data,nLedsOrOffset,nLedsIfOffset);
+ }
+ //@}
#endif
+ /// Set the global brightness scaling
+ /// @param scale a 0-255 value for how much to scale all leds before writing them out
void setBrightness(uint8_t scale) { m_Scale = scale; }
+
+ /// Get the current global brightness setting
+ /// @returns the current global brightness value
uint8_t getBrightness() { return m_Scale; }
/// Update all our controllers with the current led colors, using the passed in brightness
+ /// @param scale temporarily override the scale
void show(uint8_t scale);
/// Update all our controllers with the current led colors
void show() { show(m_Scale); }
+ /// clear the leds, optionally wiping the local array of data as well
+ /// @param writeData whether or not to write into the local data array as well
void clear(boolean writeData = false);
+ /// clear out the local data array
void clearData();
+ /// Set all leds on all controllers to the given color/scale
+ /// @param color what color to set the leds to
+ /// @param scale what brightness scale to show at
void showColor(const struct CRGB & color, uint8_t scale);
+ /// Set all leds on all controllers to the given color
+ /// @param color what color to set the leds to
void showColor(const struct CRGB & color) { showColor(color, m_Scale); }
+ /// Delay for the given number of milliseconds. Provided to allow the library to be used on platforms
+ /// that don't have a delay function (to allow code to be more portable)
+ /// @param ms the number of milliseconds to pause for
void delay(unsigned long ms);
+ /// Set a global color temperature. Sets the color temperature for all added led strips, overriding whatever
+ /// previous color temperature those controllers may have had
+ /// @param temp A CRGB structure describing the color temperature
void setTemperature(const struct CRGB & temp);
+
+ /// Set a global color correction. Sets the color correction for all added led strips,
+ /// overriding whatever previous color correction those controllers may have had.
+ /// @param correction A CRGB structure describin the color correction.
void setCorrection(const struct CRGB & correction);
+
+ /// Set the dithering mode. Sets the dithering mode for all added led strips, overriding
+ /// whatever previous dithering option those controllers may have had.
+ /// @param ditherMode - what type of dithering to use, either BINARY_DITHER or DISABLE_DITHER
void setDither(uint8_t ditherMode = BINARY_DITHER);
- // for debugging, will keep track of time between calls to countFPS, and every
- // nFrames calls, it will update an internal counter for the current FPS.
+ /// Set the maximum refresh rate. This is global for all leds. Attempts to
+ /// call show faster than this rate will simply wait. Note that the refresh rate
+ /// defaults to the slowest refresh rate of all the leds added through addLeds. If
+ /// you wish to set/override this rate, be sure to call setMaxRefreshRate _after_
+ /// adding all of your leds.
+ /// @param refresh - maximum refresh rate in hz
+ /// @param constrain - constrain refresh rate to the slowest speed yet set
+ void setMaxRefreshRate(uint16_t refresh, bool constrain=false);
+
+ /// for debugging, will keep track of time between calls to countFPS, and every
+ /// nFrames calls, it will update an internal counter for the current FPS.
+ /// @todo make this a rolling counter
+ /// @param nFrames - how many frames to time for determining FPS
void countFPS(int nFrames=25);
- // Get the number of frames/second being written out
+
+ /// Get the number of frames/second being written out
+ /// @returns the most recently computed FPS value
uint16_t getFPS() { return m_nFPS; }
- // returns the number of controllers (strips) that have been added with addLeds
+ /// Get how many controllers have been registered
+ /// @returns the number of controllers (strips) that have been added with addLeds
int count();
- // returns the Nth controller
+ /// Get a reference to a registered controller
+ /// @returns a reference to the Nth controller
CLEDController & operator[](int x);
- // Convenience functions for single-strip setups:
-
- // returns the number of LEDs in the first strip
+ /// Get the number of leds in the first controller
+ /// @returns the number of LEDs in the first controller
int size() { return (*this)[0].size(); }
- // returns pointer to the CRGB buffer for the first strip
+ /// Get a pointer to led data for the first controller
+ /// @returns pointer to the CRGB buffer for the first controller
CRGB *leds() { return (*this)[0].leds(); }
};
-extern CFastLED & FastSPI_LED;
-extern CFastLED & FastSPI_LED2;
-extern CFastLED & FastLED;
-extern CFastLED LEDS;
+#define FastSPI_LED FastLED
+#define FastSPI_LED2 FastLED
+#ifndef LEDS
+#define LEDS FastLED
+#endif
+
+extern CFastLED FastLED;
+
+// Warnings for undefined things
+#ifndef HAS_HARDWARE_PIN_SUPPORT
+#warning "No pin/port mappings found, pin access will be slightly slower. See fastpin.h for info."
+#define NO_HARDWARE_PIN_SUPPORT
+#endif
+
+
+FASTLED_NAMESPACE_END
#endif
diff --git a/FastSPI_LED2.h b/FastSPI_LED2.h
deleted file mode 100644
index 55d9a519..00000000
--- a/FastSPI_LED2.h
+++ /dev/null
@@ -1,2 +0,0 @@
-#warning "This file is going away, please us FastLED.h in the future!"
-#include<FastLED.h>
diff --git a/PORTING.md b/PORTING.md
new file mode 100644
index 00000000..2b4ade2e
--- /dev/null
+++ b/PORTING.md
@@ -0,0 +1,29 @@
+=New platform porting guide=
+
+== Setting up the basic files/folders ==
+
+* Create platform directory (e.g. platforms/arm/kl26)
+* Create configuration header led_sysdefs_arm_kl26.h:
+ * Define platform flags (like FASTLED_ARM/FASTLED_TEENSY)
+ * Define configuration parameters re: interrupts, or clock doubling
+ * Include extar system header files if needed
+* Create main platform include, fastled_arm_kl26.h
+ * Include the various other header files as needed
+* Modify led_sysdefs.h to conditionally include platform sysdefs header file
+* Modify platforms.h to conditionally include platform fastled header
+
+== Porting fastpin.h ==
+
+The heart of the FastLED library is the fast pin accesss. This is a templated class that provides 1-2 cycle pin access, bypassing digital write and other such things. As such, this will usually be the first bit of the library that you will want to port when moving to a new platform. Once you have FastPIN up and running then you can do some basic work like testing toggles or running bit-bang'd SPI output.
+
+There's two low level FastPin classes. There's the base FastPIN template class, and then there is FastPinBB which is for bit-banded access on those MCUs that support bitbanding. Note that the bitband class is optional and primarily useful in the implementation of other functionality internal to the platform. This file is also where you would do the pin to port/bit mapping defines.
+
+Explaining how the macros work and should be used is currently beyond the scope of this document.
+
+== Porting fastspi.h ==
+
+This is where you define the low level interface to the hardware SPI system (including a writePixels method that does a bunch of housekeeping for writing led data). Use the fastspi_nop.h file as a reference for the methods that need to be implemented. There are ofteh other useful methods that can help with the internals of the SPI code, I recommend taking a look at how the various platforms implement their SPI classes.
+
+== Porting clockless.h ==
+
+This is where you define the code for the clockless controllers. Across ARM platforms this will usually be fairly similar - though different arm platforms will have different clock sources that you can/should use.
diff --git a/README.md b/README.md
index dd7d2a20..0f1e3bbf 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,15 @@
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/FastLED/public)
-IMPORTANT NOTE: If you are building for AVR based systems, it is possible that using any version of the arduino IDE 1.5.7 or later and/or GCC 4.8 may introduce some issues. I believe I have fixed them, but if you run into issues using these platforms, please file an issue at http://fastled.io/issues - thank you! If you are building for the Due, Arduino 1.5.7 & later introduces issues that have currently been fixed on the FastLED 3.1 branch.
+IMPORTANT NOTE: For AVR based systems, avr-gcc 4.8.x is supported, as is avr-gcc 4.3 and earlier. There are known issues with avr-gcc 4.7 and timing based chipsets like the WS2812B. If you are using a linux system make sure you are using avr-gcc 4.8.x not avr-gcc 4.7.x.
-FastLED 3.0
+FastLED 3.1
===========
-IMPORTANT NOTE: If you are building for AVR based systems, please do not use any version of the arduino
-IDE 1.5.7 or later yet. It messes with some of the asm output which will cause you problems.
-
This is a library for easily & efficiently controlling a wide variety of LED chipsets, like the ones
-sold by adafruit (Neopixel, LPD8806), Sparkfun (WS2801), and aliexpress. In addition to writing to the
+sold by adafruit (Neopixel, DotStar, LPD8806), Sparkfun (WS2801), and aliexpress. In addition to writing to the
leds, this library also includes a number of functions for high-performing 8bit math for manipulating
your RGB values, as well as low level classes for abstracting out access to pins and SPI hardware, while
-still keeping things as fast as possible.
+still keeping things as fast as possible. Tested with Arduino up to 1.6.5 from arduino.cc.
Quick note for people installing from GitHub repo zips, rename the folder FastLED before copying it to your Arduino/libraries folder. Github likes putting -branchname into the name of the folder, which unfortunately, makes Arduino cranky!
@@ -34,8 +31,8 @@ How quickly can you get up and running with the library? Here's a simple blink
#define NUM_LEDS 60
CRGB leds[NUM_LEDS];
void setup() { FastLED.addLeds<NEOPIXEL, 6>(leds, NUM_LEDS); }
- void loop() {
- leds[0] = CRGB::White; FastLED.show(); delay(30);
+ void loop() {
+ leds[0] = CRGB::White; FastLED.show(); delay(30);
leds[0] = CRGB::Black; FastLED.show(); delay(30);
}
@@ -43,7 +40,8 @@ How quickly can you get up and running with the library? Here's a simple blink
Here's a list of all the LED chipsets are supported. More details on the led chipsets are included *TODO: Link to wiki page*
-* Adafruit's Neopixel - aka the WS2812B (also WS2811/WS2812, also suppored in lo-speed mode) - a 3 wire addressable led chipset
+* Adafruit's DotStars - AKA the APA102
+* Adafruit's Neopixel - aka the WS2812B (also WS2811/WS2812, also supported in lo-speed mode) - a 3 wire addressable led chipset
* TM1809/4 - 3 wire chipset, cheaply available on aliexpress.com
* TM1803 - 3 wire chipset, sold by radio shack
* UCS1903 - another 3 wire led chipset, cheap
@@ -67,10 +65,13 @@ Right now the library is supported on a variety of arduino compatable platforms.
* Arduino & compatibles - straight up arduino devices, uno, duo, leonardo, mega, nano, etc...
* Arduino Yún
* Adafruit Trinket & Gemma - Trinket Pro may be supported, but haven't tested to confirm yet
-* Teensy 2, Teensy++ 2, Teensy 3.1 - arduino compataible from pjrc.com with some extra goodies (note the teensy 3 is ARM, not AVR!)
+* Teensy 2, Teensy++ 2, Teensy 3.0, Teensy 3.1, Teensy LC - arduino compataible from pjrc.com with some extra goodies (note the teensy 3, 3.1, and LC are ARM, not AVR!)
* Arduino Due and the digistump DigiX
+* RFDuino
+* SparkCore
+* Arduino Zero
-What types of platforms are we thinking about supporting in the future? Here's a short list: RFDuino, SparkCore, MSP430, ChipKit32, Maple, Beagleboard
+What types of platforms are we thinking about supporting in the future? Here's a short list: ChipKit32, Maple, Beagleboard
## What about that name?
@@ -82,4 +83,3 @@ Check out the official site http://fastled.io for links to documentation, issues
*TODO* - get candy
-
diff --git a/bitswap.h b/bitswap.h
new file mode 100644
index 00000000..3af7d1b2
--- /dev/null
+++ b/bitswap.h
@@ -0,0 +1,272 @@
+#ifndef __INC_BITSWAP_H
+#define __INC_BITSWAP_H
+
+FASTLED_NAMESPACE_BEGIN
+
+///@file bitswap.h
+///Functions for rotating bits/bytes
+
+///@defgroup Bitswap Bit swapping/rotate
+///Functions for doing a rotation of bits/bytes used by parallel output
+///@{
+#ifdef FASTLED_ARM
+/// structure representing 8 bits of access
+typedef union {
+ uint8_t raw;
+ struct {
+ uint32_t a0:1;
+ uint32_t a1:1;
+ uint32_t a2:1;
+ uint32_t a3:1;
+ uint32_t a4:1;
+ uint32_t a5:1;
+ uint32_t a6:1;
+ uint32_t a7:1;
+ };
+} just8bits;
+
+/// structure representing 32 bits of access
+typedef struct {
+ uint32_t a0:1;
+ uint32_t a1:1;
+ uint32_t a2:1;
+ uint32_t a3:1;
+ uint32_t a4:1;
+ uint32_t a5:1;
+ uint32_t a6:1;
+ uint32_t a7:1;
+ uint32_t b0:1;
+ uint32_t b1:1;
+ uint32_t b2:1;
+ uint32_t b3:1;
+ uint32_t b4:1;
+ uint32_t b5:1;
+ uint32_t b6:1;
+ uint32_t b7:1;
+ uint32_t c0:1;
+ uint32_t c1:1;
+ uint32_t c2:1;
+ uint32_t c3:1;
+ uint32_t c4:1;
+ uint32_t c5:1;
+ uint32_t c6:1;
+ uint32_t c7:1;
+ uint32_t d0:1;
+ uint32_t d1:1;
+ uint32_t d2:1;
+ uint32_t d3:1;
+ uint32_t d4:1;
+ uint32_t d5:1;
+ uint32_t d6:1;
+ uint32_t d7:1;
+} sub4;
+
+/// union containing a full 8 bytes to swap the bit orientation on
+typedef union {
+ uint32_t word[2];
+ uint8_t bytes[8];
+ struct {
+ sub4 a;
+ sub4 b;
+ };
+} bitswap_type;
+
+
+#define SWAPSA(X,N) out. X ## 0 = in.a.a ## N; \
+ out. X ## 1 = in.a.b ## N; \
+ out. X ## 2 = in.a.c ## N; \
+ out. X ## 3 = in.a.d ## N;
+
+#define SWAPSB(X,N) out. X ## 0 = in.b.a ## N; \
+ out. X ## 1 = in.b.b ## N; \
+ out. X ## 2 = in.b.c ## N; \
+ out. X ## 3 = in.b.d ## N;
+
+#define SWAPS(X,N) out. X ## 0 = in.a.a ## N; \
+ out. X ## 1 = in.a.b ## N; \
+ out. X ## 2 = in.a.c ## N; \
+ out. X ## 3 = in.a.d ## N; \
+ out. X ## 4 = in.b.a ## N; \
+ out. X ## 5 = in.b.b ## N; \
+ out. X ## 6 = in.b.c ## N; \
+ out. X ## 7 = in.b.d ## N;
+
+
+/// Do an 8byte by 8bit rotation
+__attribute__((always_inline)) inline void swapbits8(bitswap_type in, bitswap_type & out) {
+
+ // SWAPS(a.a,7);
+ // SWAPS(a.b,6);
+ // SWAPS(a.c,5);
+ // SWAPS(a.d,4);
+ // SWAPS(b.a,3);
+ // SWAPS(b.b,2);
+ // SWAPS(b.c,1);
+ // SWAPS(b.d,0);
+
+ // SWAPSA(a.a,7);
+ // SWAPSA(a.b,6);
+ // SWAPSA(a.c,5);
+ // SWAPSA(a.d,4);
+ //
+ // SWAPSB(a.a,7);
+ // SWAPSB(a.b,6);
+ // SWAPSB(a.c,5);
+ // SWAPSB(a.d,4);
+ //
+ // SWAPSA(b.a,3);
+ // SWAPSA(b.b,2);
+ // SWAPSA(b.c,1);
+ // SWAPSA(b.d,0);
+ // //
+ // SWAPSB(b.a,3);
+ // SWAPSB(b.b,2);
+ // SWAPSB(b.c,1);
+ // SWAPSB(b.d,0);
+
+ for(int i = 0; i < 8; i++) {
+ just8bits work;
+ work.a3 = in.word[0] >> 31;
+ work.a2 = in.word[0] >> 23;
+ work.a1 = in.word[0] >> 15;
+ work.a0 = in.word[0] >> 7;
+ in.word[0] <<= 1;
+ work.a7 = in.word[1] >> 31;
+ work.a6 = in.word[1] >> 23;
+ work.a5 = in.word[1] >> 15;
+ work.a4 = in.word[1] >> 7;
+ in.word[1] <<= 1;
+ out.bytes[i] = work.raw;
+ }
+}
+
+/// Slow version of the 8 byte by 8 bit rotation
+__attribute__((always_inline)) inline void slowswap(unsigned char *A, unsigned char *B) {
+
+ for(int row = 0; row < 7; row++) {
+ uint8_t x = A[row];
+
+ uint8_t bit = (1<<row);
+ unsigned char *p = B;
+ for(uint32_t mask = 1<<7 ; mask ; mask >>= 1) {
+ if(x & mask) {
+ *p++ |= bit;
+ } else {
+ *p++ &= ~bit;
+ }
+ }
+ // B[7] |= (x & 0x01) << row; x >>= 1;
+ // B[6] |= (x & 0x01) << row; x >>= 1;
+ // B[5] |= (x & 0x01) << row; x >>= 1;
+ // B[4] |= (x & 0x01) << row; x >>= 1;
+ // B[3] |= (x & 0x01) << row; x >>= 1;
+ // B[2] |= (x & 0x01) << row; x >>= 1;
+ // B[1] |= (x & 0x01) << row; x >>= 1;
+ // B[0] |= (x & 0x01) << row; x >>= 1;
+ }
+}
+
+/// Simplified form of bits rotating function. Based on code found here - http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt - rotating
+/// data into LSB for a faster write (the code using this data can happily walk the array backwards)
+__attribute__((always_inline)) inline void transpose8x1(unsigned char *A, unsigned char *B) {
+ uint32_t x, y, t;
+
+ // Load the array and pack it into x and y.
+ y = *(unsigned int*)(A);
+ x = *(unsigned int*)(A+4);
+
+ // pre-transform x
+ t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
+ t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
+
+ // pre-transform y
+ t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
+ t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
+
+ // final transform
+ t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
+ y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
+ x = t;
+
+ *((uint32_t*)B) = y;
+ *((uint32_t*)(B+4)) = x;
+}
+
+/// Simplified form of bits rotating function. Based on code found here - http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt
+__attribute__((always_inline)) inline void transpose8x1_MSB(unsigned char *A, unsigned char *B) {
+ uint32_t x, y, t;
+
+ // Load the array and pack it into x and y.
+ y = *(unsigned int*)(A);
+ x = *(unsigned int*)(A+4);
+
+ // pre-transform x
+ t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
+ t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
+
+ // pre-transform y
+ t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
+ t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
+
+ // final transform
+ t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
+ y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
+ x = t;
+
+ B[7] = y; y >>= 8;
+ B[6] = y; y >>= 8;
+ B[5] = y; y >>= 8;
+ B[4] = y;
+
+ B[3] = x; x >>= 8;
+ B[2] = x; x >>= 8;
+ B[1] = x; x >>= 8;
+ B[0] = x; /* */
+}
+
+/// templated bit-rotating function. Based on code found here - http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt
+template<int m, int n>
+__attribute__((always_inline)) inline void transpose8(unsigned char *A, unsigned char *B) {
+ uint32_t x, y, t;
+
+ // Load the array and pack it into x and y.
+ if(m == 1) {
+ y = *(unsigned int*)(A);
+ x = *(unsigned int*)(A+4);
+ } else {
+ x = (A[0]<<24) | (A[m]<<16) | (A[2*m]<<8) | A[3*m];
+ y = (A[4*m]<<24) | (A[5*m]<<16) | (A[6*m]<<8) | A[7*m];
+ }
+
+ // pre-transform x
+ t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
+ t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
+
+ // pre-transform y
+ t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
+ t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
+
+ // final transform
+ t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
+ y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
+ x = t;
+
+ B[7*n] = y; y >>= 8;
+ B[6*n] = y; y >>= 8;
+ B[5*n] = y; y >>= 8;
+ B[4*n] = y;
+
+ B[3*n] = x; x >>= 8;
+ B[2*n] = x; x >>= 8;
+ B[n] = x; x >>= 8;
+ B[0] = x;
+ // B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x>>0;
+ // B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y>>0;
+}
+
+#endif
+
+FASTLED_NAMESPACE_END
+
+///@}
+#endif
diff --git a/block_clockless.h b/block_clockless.h
deleted file mode 100644
index d561239c..00000000
--- a/block_clockless.h
+++ /dev/null
@@ -1,232 +0,0 @@
- #ifndef __INC_BLOCK_CLOCKLESS_H
-#define __INC_BLOCK_CLOCKLESS_H
-
-#include "controller.h"
-#include "lib8tion.h"
-#include "led_sysdefs.h"
-#include "delay.h"
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. The first point
-// is where the line is raised hi. The second pointsnt is where the line is dropped low for a zero. The third point is where the
-// line is dropped low for a one. T1, T2, and T3 correspond to the timings for those three in clock cycles.
-//
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-#define PORT_MASK 0x77EFF3FE
-#define SKIPLIST ~PORT_MASK
-
-#if defined(__SAM3X8E__)
-#define HAS_BLOCKLESS 1
-template <int NUM_LANES, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int WAIT_TIME = 50>
-class BlockClocklessController : public CLEDController {
- typedef typename FastPinBB<1>::port_ptr_t data_ptr_t;
- typedef typename FastPinBB<1>::port_t data_t;
-
- CMinWait<WAIT_TIME> mWait;
- uint32_t *m_pBuffer;
-
- void transformData(uint8_t *leddata, int num_leds, uint8_t scale = 255) {
- if(m_pBuffer == NULL) {
- m_pBuffer = (uint32_t*)malloc(4 * 8 * 3 * num_leds);
- }
-
- uint32_t *outputdata = m_pBuffer;
- for(register int i = 0; i < num_leds; i++) {
- register byte rgboffset = RGB_BYTE0(RGB_ORDER);
- for(int rgb = 0; rgb < 3; rgb++) {
- register uint32_t mask = 0x01; // 0x01;
- register uint32_t output[8] = {0,0,0,0,0,0,0,0};
-
- // set the base address to skip through
- uint8_t *database = leddata + (3*i) + rgboffset;
- for(int j = 0; j < NUM_LANES; j++) {
- register uint8_t byte = ~scale8(*database, scale);
- if(byte & 0x80) { output[0] |= mask; }
- if(byte & 0x40) { output[1] |= mask; }
- if(byte & 0x20) { output[2] |= mask; }
- if(byte & 0x10) { output[3] |= mask; }
- if(byte & 0x08) { output[4] |= mask; }
- if(byte & 0x04) { output[5] |= mask; }
- if(byte & 0x02) { output[6] |= mask; }
- if(byte & 0x01) { output[7] |= mask; }
-
- // SKIPLIST is a 32 bit constant that contains the bit positions that are off limits, courtesy of port stupidities
- do { mask <<= 1; } while(SKIPLIST & mask);
-
- // move the data pointer forward a lane
- database += (num_leds * 3);
-
- // cycle between rgb ordering according to RGB order set (may need per-lane rgb ordering? ugh ugh ugh, i hope not!)
- rgboffset = (rgboffset == RGB_BYTE0(RGB_ORDER)) ? RGB_BYTE1(RGB_ORDER) : (rgboffset == RGB_BYTE1(RGB_ORDER) ? RGB_BYTE2(RGB_ORDER) : RGB_BYTE0(RGB_ORDER));
-
- // copy data out
- for(int j = 0; j < 8; j++) { *outputdata++ = output[j]; }
- }
- }
- }
- }
-
-public:
- virtual void init() {
- //FastPinBB<DATA_PIN>::setOutput();
- uint8_t pins[] = { 33, 34, 35, 36, 37, 38, 39, 40, 41, 51, 50, 49, 48, 47, 46,45, 44, 9, 8, 7, 6, 5, 4, 3, 10, 72, 0 };
- int i = 0;
- while(pins[i]) { pinMode(pins[i++], OUTPUT); }
- m_pBuffer = NULL;
- }
-
- virtual void clearLeds(int nLeds) {
- showColor(CRGB(0, 0, 0), nLeds, 0);
- }
-
- // set all the leds on the controller to a given color
- virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
- SysClockSaver savedClock(T1 + T2 + T3);
-
- // showRGBInternal<0, false>(nLeds, scale, (const byte*)&data);
-
- // Adjust the timer
- long microsTaken = nLeds * CLKS_TO_MICROS(24 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- savedClock.restore();
- sei();
- mWait.mark();
- }
-
- virtual void show(const struct CRGB *rgbdata, int nLeds, uint8_t scale = 255) {
- transformData((uint8_t*)rgbdata, nLeds, scale);
- mWait.wait();
- cli();
- SysClockSaver savedClock(T1 + T2 + T3);
-
- // FastPinBB<DATA_PIN>::hi(); delay(1); FastPinBB<DATA_PIN>::lo();
- showRGBInternal<0, true>(nLeds);
-
- // Adjust the timer
- long microsTaken = nLeds * CLKS_TO_MICROS(24 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- savedClock.restore();
- sei();
- mWait.mark();
- }
-
-#ifdef SUPPORT_ARGB
- virtual void show(const struct CARGB *rgbdata, int nLeds, uint8_t scale = 255) {
- transformData((uint8_t*)rgbdata, nLeds, scale);
-
- mWait.wait();
- cli();
- SysClockSaver savedClock(T1 + T2 + T3);
-
- showRGBInternal<1, true>(nLeds, scale, (const byte*)rgbdata);
-
- // Adjust the timer
- long microsTaken = nLeds * CLKS_TO_MICROS(24 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- savedClock.restore();
- sei();
- mWait.mark();
- }
-#endif
-
-// I hate using defines for these, should find a better representation at some point
-#define _CTRL CTPTR[0]
-#define _LOAD CTPTR[1]
-#define _VAL CTPTR[2]
-
- __attribute__((always_inline)) static inline void wait_loop_start(register volatile uint32_t *CTPTR) {
- __asm__ __volatile__ (
- "L_%=: ldr.w r8, [%0]\n"
- " tst.w r8, #65536\n"
- " beq.n L_%=\n"
- : /* no outputs */
- : "r" (CTPTR)
- : "r8"
- );
- }
-
- template<int MARK> __attribute__((always_inline)) static inline void wait_loop_mark(register volatile uint32_t *CTPTR) {
- __asm__ __volatile__ (
- "L_%=: ldr.w r8, [%0, #8]\n"
- " cmp.w r8, %1\n"
- " bhi.n L_%=\n"
- : /* no outputs */
- : "r" (CTPTR), "I" (MARK)
- : "r8"
- );
- }
-
- __attribute__((always_inline)) static inline void mark_port(register data_ptr_t port, register int val) {
- __asm__ __volatile__ (
- " str.w %0, [%1]\n"
- : /* no outputs */
- : "r" (val), "r" (port)
- );
- }
-#define AT_BIT_START(X) wait_loop_start(CTPTR); X;
-#define AT_MARK(X) wait_loop_mark<T1_MARK>(CTPTR); { X; }
-#define AT_END(X) wait_loop_mark<T2_MARK>(CTPTR); { X; }
-
-// #define AT_BIT_START(X) while(!(_CTRL & SysTick_CTRL_COUNTFLAG_Msk)); { X; }
-// #define AT_MARK(X) while(_VAL > T1_MARK); { X; }
-// #define AT_END(X) while(_VAL > T2_MARK); { X; }
-
-//#define AT_MARK(X) delayclocks_until<T1_MARK>(_VAL); X;
-//#define AT_END(X) delayclocks_until<T2_MARK>(_VAL); X;
-
-#define TOTAL (T1 + T2 + T3)
-
-#define T1_MARK (TOTAL - T1)
-#define T2_MARK (T1_MARK - T2)
- template<int MARK> __attribute__((always_inline)) static inline void delayclocks_until(register byte b) {
- __asm__ __volatile__ (
- " sub %0, %0, %1\n"
- "L_%=: subs %0, %0, #2\n"
- " bcs.n L_%=\n"
- : /* no outputs */
- : "r" (b), "I" (MARK)
- : /* no clobbers */
- );
-
- }
-
-#define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) )
-
- // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
- // gcc will use register Y for the this pointer.
- template<int SKIP, bool ADVANCE> void showRGBInternal(register int nLeds) {
- register uint32_t *data = m_pBuffer;
- register uint32_t *end = data + 8*(nLeds*3);
-
- register volatile uint32_t *CTPTR asm("r6")= &SysTick->CTRL; FORCE_REFERENCE(CTPTR);
-
- // Setup and start the clock
- _LOAD = TOTAL;
- _VAL = 0;
- _CTRL |= SysTick_CTRL_CLKSOURCE_Msk;
- _CTRL |= SysTick_CTRL_ENABLE_Msk;
-
- // read to clear the loop flag
- _CTRL;
-
- while(data < end) {
- register uint32_t d = *data++;
- // turn everything in the port on at the start
- AT_BIT_START(REG_PIOC_SODR = PORT_MASK);
-
- // part way through early clear the ones that should be 0 bits
- AT_MARK(REG_PIOC_CODR = d);
-
- // now all the way through zero everyone
- AT_END(REG_PIOC_CODR= PORT_MASK);
- };
- }
-};
-
-#endif
-
-#endif \ No newline at end of file
diff --git a/chipsets.h b/chipsets.h
index 5e15baa6..32c64bb5 100644
--- a/chipsets.h
+++ b/chipsets.h
@@ -2,14 +2,29 @@
#define __INC_CHIPSETS_H
#include "pixeltypes.h"
-#include "clockless.h"
+///@file chipsets.h
+/// contains the bulk of the definitions for the various LED chipsets supported.
+
+FASTLED_NAMESPACE_BEGIN
+///@defgroup chipsets
+/// Implementations of CLEDController classes for various led chipsets.
+///
+///@{
+
+///@name Clocked chipsets - nominally SPI based these chipsets have a data and a clock line.
+///@{
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// LPD8806 controller class - takes data/clock/select pin values (N.B. should take an SPI definition?)
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/// LPD8806 controller class.
+/// @tparam DATA_PIN the data pin for these leds
+/// @tparam CLOCK_PIN the clock pin for these leds
+/// @tparam RGB_ORDER the RGB ordering for these leds
+/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(12)
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(12) >
class LPD8806Controller : public CLEDController {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
@@ -49,6 +64,7 @@ public:
mSPI.select();
mSPI.writeBytesValueRaw(0x80, nLeds * 3);
mSPI.writeBytesValueRaw(0, ((nLeds*3+63)>>6));
+ mSPI.waitFully();
mSPI.release();
}
@@ -78,6 +94,11 @@ protected:
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/// WS2801 controller class.
+/// @tparam DATA_PIN the data pin for these leds
+/// @tparam CLOCK_PIN the clock pin for these leds
+/// @tparam RGB_ORDER the RGB ordering for these leds
+/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(1)
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(1)>
class WS2801Controller : public CLEDController {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
@@ -129,13 +150,18 @@ class WS2803Controller : public WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER,
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = BGR, uint8_t SPI_SPEED = DATA_RATE_MHZ(12)>
+/// APA102 controller class.
+/// @tparam DATA_PIN the data pin for these leds
+/// @tparam CLOCK_PIN the clock pin for these leds
+/// @tparam RGB_ORDER the RGB ordering for these leds
+/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(24)
+template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = BGR, uint8_t SPI_SPEED = DATA_RATE_MHZ(24)>
class APA102Controller : public CLEDController {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
SPI mSPI;
void startBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); }
- void endBoundary() { /*mSPI.writeWord(0xFFFF); mSPI.writeWord(0xFFFF); */}
+ void endBoundary(int nLeds) { int nBytes = (nLeds/32); do { mSPI.writeByte(0xFF); mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); } while(nBytes--); }
inline void writeLed(uint8_t b0, uint8_t b1, uint8_t b2) __attribute__((always_inline)) {
mSPI.writeByte(0xFF); mSPI.writeByte(b0); mSPI.writeByte(b1); mSPI.writeByte(b2);
@@ -160,11 +186,15 @@ protected:
mSPI.select();
startBoundary();
- while(nLeds--) {
- writeLed(pixels.loadAndScale0(), pixels.loadAndScale1(), pixels.loadAndScale2());
+ for(int i = 0; i < nLeds; i++) {
+ uint8_t b = pixels.loadAndScale0();
+ mSPI.writeWord(0xFF00 | b);
+ uint16_t w = pixels.loadAndScale1() << 8;
+ w |= pixels.loadAndScale2();
+ mSPI.writeWord(w);
pixels.stepDithering();
}
- endBoundary();
+ endBoundary(nLeds);
mSPI.waitFully();
mSPI.release();
@@ -177,12 +207,16 @@ protected:
startBoundary();
for(int i = 0; i < nLeds; i++) {
- writeLed(pixels.loadAndScale0(), pixels.loadAndScale1(), pixels.loadAndScale2());
+ uint16_t b = 0xFF00 | (uint16_t)pixels.loadAndScale0();
+ mSPI.writeWord(b);
+ uint16_t w = pixels.loadAndScale1() << 8;
+ w |= pixels.loadAndScale2();
+ mSPI.writeWord(w);
pixels.advanceData();
pixels.stepDithering();
}
- endBoundary();
-
+ endBoundary(nLeds);
+ mSPI.waitFully();
mSPI.release();
}
@@ -194,12 +228,15 @@ protected:
startBoundary();
for(int i = 0; i < nLeds; i++) {
- writeLed(pixels.loadAndScale0(), pixels.loadAndScale1(), pixels.loadAndScale2());
+ mSPI.writeByte(0xFF);
+ uint8_t b = pixels.loadAndScale0(); mSPI.writeByte(b);
+ b = pixels.loadAndScale1(); mSPI.writeByte(b);
+ b = pixels.loadAndScale2(); mSPI.writeByte(b);
pixels.advanceData();
pixels.stepDithering();
}
- endBoundary();
-
+ endBoundary(nLeds);
+ mSPI.waitFully();
mSPI.release();
}
#endif
@@ -212,6 +249,11 @@ protected:
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/// P9813 controller class.
+/// @tparam DATA_PIN the data pin for these leds
+/// @tparam CLOCK_PIN the clock pin for these leds
+/// @tparam RGB_ORDER the RGB ordering for these leds
+/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(10)
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(10)>
class P9813Controller : public CLEDController {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
@@ -265,6 +307,7 @@ protected:
pixels.stepDithering();
}
writeBoundary();
+ mSPI.waitFully();
mSPI.release();
}
@@ -282,6 +325,7 @@ protected:
pixels.stepDithering();
}
writeBoundary();
+ mSPI.waitFully();
mSPI.release();
}
@@ -295,6 +339,11 @@ protected:
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/// SM16716 controller class.
+/// @tparam DATA_PIN the data pin for these leds
+/// @tparam CLOCK_PIN the clock pin for these leds
+/// @tparam RGB_ORDER the RGB ordering for these leds
+/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(16)
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(16)>
class SM16716Controller : public CLEDController {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
@@ -357,13 +406,18 @@ protected:
}
#endif
};
-
+/// @}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Clockless template instantiations - see clockless.h for how the timing values are used
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#ifdef FASTLED_HAS_CLOCKLESS
+/// @name clockless controllers
+/// Provides timing definitions for the variety of clockless controllers supplied by the library.
+/// @{
+
// We want to force all avr's to use the Trinket controller when running at 8Mhz, because even the 328's at 8Mhz
// need the more tightly defined timeframes.
#if (F_CPU == 8000000 || F_CPU == 16000000 || F_CPU == 24000000) // || F_CPU == 48000000 || F_CPU == 96000000) // 125ns/clock
@@ -374,8 +428,10 @@ class LPD1886Controller1250Khz : public ClocklessController<DATA_PIN, 2 * FMUL,
// WS2811@800khz 2 clocks, 5 clocks, 3 clocks
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class WS2811Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER> {};
-//class WS2811Controller800Khz : public ClocklessController<DATA_PIN, 3 * FMUL, 4 * FMUL, 3 * FMUL, RGB_ORDER> {};
+class WS2812Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER> {};
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class WS2811Controller800Khz : public ClocklessController<DATA_PIN, 3 * FMUL, 4 * FMUL, 3 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2811Controller400Khz : public ClocklessController<DATA_PIN, 4 * FMUL, 10 * FMUL, 6 * FMUL, RGB_ORDER> {};
@@ -387,6 +443,9 @@ template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903BController800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 4 * FMUL, 4 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class UCS1904Controller800Khz : public ClocklessController<DATA_PIN, 3 * FMUL, 3 * FMUL, 4 * FMUL, RGB_ORDER> {};
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1809Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
@@ -420,19 +479,26 @@ class GW6205Controller800Khz : public ClocklessController<DATA_PIN, NS(400), NS(
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903Controller400Khz : public ClocklessController<DATA_PIN, NS(500), NS(1500), NS(500), RGB_ORDER> {};
#if NO_TIME(500, 1500, 500)
-#warning "Not enough clock cycles available for the UCS103@400khz"
+#warning "Not enough clock cycles available for the UCS1903@400khz"
#endif
// UCS1903B - 400ns, 450ns, 450ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903BController800Khz : public ClocklessController<DATA_PIN, NS(400), NS(450), NS(450), RGB_ORDER> {};
#if NO_TIME(400, 450, 450)
-#warning "Not enough clock cycles available for the UCS103B@800khz"
+#warning "Not enough clock cycles available for the UCS1903B@800khz"
+#endif
+
+// UCS1904 - 400ns, 400ns, 450ns
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class UCS1904Controller800Khz : public ClocklessController<DATA_PIN, NS(400), NS(400), NS(450), RGB_ORDER> {};
+#if NO_TIME(400, 400, 450)
+#warning "Not enough clock cycles available for the UCS1904@800khz"
#endif
// TM1809 - 350ns, 350ns, 550ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class TM1809Controller800Khz : public ClocklessController<DATA_PIN, NS(350), NS(350), NS(550), RGB_ORDER> {};
+class TM1809Controller800Khz : public ClocklessController<DATA_PIN, NS(350), NS(350), NS(450), RGB_ORDER> {};
#if NO_TIME(350, 350, 550)
#warning "Not enough clock cycles available for the TM1809"
#endif
@@ -444,6 +510,13 @@ class WS2811Controller800Khz : public ClocklessController<DATA_PIN, NS(320), NS(
#warning "Not enough clock cycles available for the WS2811 (800khz)"
#endif
+// WS2812 - 250ns, 625ns, 375ns
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class WS2812Controller800Khz : public ClocklessController<DATA_PIN, NS(250), NS(625), NS(375), RGB_ORDER> {};
+#if NO_TIME(250, 625, 375)
+#warning "Not enough clock cycles available for the WS2812 (800khz)"
+#endif
+
// WS2811@400khz - 800ns, 800ns, 900ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2811Controller400Khz : public ClocklessController<DATA_PIN, NS(800), NS(800), NS(900), RGB_ORDER> {};
@@ -474,5 +547,10 @@ class LPD1886Controller1250Khz : public ClocklessController<DATA_PIN, NS(200), N
#endif
#endif
+///@}
+
+#endif
+///@}
+FASTLED_NAMESPACE_END
#endif
diff --git a/clockless.h b/clockless.h
deleted file mode 100644
index b3fabbc5..00000000
--- a/clockless.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef __INC_CLOCKLESS_H
-#define __INC_CLOCKLESS_H
-
-#include "controller.h"
-#include "lib8tion.h"
-#include "led_sysdefs.h"
-#include "delay.h"
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. The first point
-// is where the line is raised hi. The second pointsnt is where the line is dropped low for a zero. The third point is where the
-// line is dropped low for a one. T1, T2, and T3 correspond to the timings for those three in clock cycles.
-//
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Convinience macros to wrap around the toggling of hi vs. lo
-#define SET_LO FLIP ? FastPin<DATA_PIN>::fastset(port, hi) : FastPin<DATA_PIN>::fastset(port, lo);
-#define SET_HI FLIP ? FastPin<DATA_PIN>::fastset(port, lo) : FastPin<DATA_PIN>::fastset(port, hi);
-
-// #include "clockless_avr.h"
-#include "clockless_trinket.h"
-#include "clockless_arm_k20.h"
-#include "clockless_arm_sam.h"
-#include "clockless2.h"
-#include "block_clockless.h"
-
-#endif
diff --git a/clockless2.h b/clockless2.h
deleted file mode 100644
index c48cde5d..00000000
--- a/clockless2.h
+++ /dev/null
@@ -1,241 +0,0 @@
-#ifndef __INC_CLOCKLESS2_H
-#define __INC_CLOCKLESS2_H
-
-#include "controller.h"
-#include "lib8tion.h"
-#include "led_sysdefs.h"
-#include "delay.h"
-
-#ifdef FASTLED_TEENSY3
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. The first point
-// is where the line is raised hi. The second pointsnt is where the line is dropped low for a zero. The third point is where the
-// line is dropped low for a one. T1, T2, and T3 correspond to the timings for those three in clock cycles.
-//
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Convinience macros to wrap around the toggling of hi vs. lo
-#define SET2_LO FLIP ? FastPin<DATA_PIN>::hi() : FastPin<DATA_PIN>::lo()
-#define SET2_HI FLIP ? FastPin<DATA_PIN>::lo() : FastPin<DATA_PIN>::hi()
-
-#define SET2_LO2 FLIP ? FastPin<DATA_PIN2>::hi() : FastPin<DATA_PIN2>::lo()
-#define SET2_HI2 FLIP ? FastPin<DATA_PIN2>::lo() : FastPin<DATA_PIN2>::hi()
-
-
-template <uint8_t DATA_PIN, uint8_t DATA_PIN2, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, bool FLIP = false, int WAIT_TIME = 50>
-class ClocklessController2 : public CLEDController {
- typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
- typedef typename FastPin<DATA_PIN>::port_t data_t;
-
- CMinWait<WAIT_TIME> mWait;
-public:
- virtual void init() {
- FastPin<DATA_PIN>::setOutput();
- FastPin<DATA_PIN2>::setOutput();
- }
-
-
- virtual void clearLeds(int nLeds) {
- showColor(CRGB(0, 0, 0), nLeds, 0);
- }
-
- // set all the leds on the controller to a given color
- virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
-
- showRGBInternal<0, false>(nLeds, scale, (const byte*)&data, (const byte*)&data);
-
- // Adjust the timer
- long microsTaken = nLeds * CLKS_TO_MICROS(24 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- sei();
- mWait.mark();
- }
-
- virtual void show(const struct CRGB *rgbdata, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
-
- showRGBInternal<0, true>(nLeds, scale, (const byte*)rgbdata, (const byte*)rgbdata);
-
- // Adjust the timer
- long microsTaken = nLeds * CLKS_TO_MICROS(24 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- sei();
- mWait.mark();
- }
-
- virtual void show(const struct CRGB *rgbdata, const struct CRGB *rgbdata2, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
-
- showRGBInternal<0, true>(nLeds, scale, (const byte*)rgbdata, (const byte*)rgbdata2 );
-
- // Adjust the timer
- long microsTaken = nLeds * CLKS_TO_MICROS(24 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- sei();
- mWait.mark();
- }
-
-#ifdef SUPPORT_ARGB
- virtual void show(const struct CARGB *rgbdata, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
-
- showRGBInternal<1, true>(nLeds, scale, (const byte*)rgbdata);
-
- // Adjust the timer
- long microsTaken = nLeds * CLKS_TO_MICROS(24 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- sei();
- mWait.mark();
- }
-#endif
-
- // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
- // gcc will use register Y for the this pointer.
- template<int SKIP, bool ADVANCE> static void showRGBInternal(register int nLeds, register uint8_t scale, register const byte *rgbdata, register const byte *rgbdata2) {
- register byte *data = (byte*)rgbdata;
- register byte *data2 = (byte*)rgbdata2;
-
- nLeds *= (3 + SKIP);
- register uint8_t *end = data + nLeds;
-
- register uint32_t b;
- register uint32_t c;
- if(ADVANCE) {
- b = data[SKIP + RGB_BYTE0(RGB_ORDER)];
- c = data2[SKIP + RGB_BYTE0(RGB_ORDER)];
- } else {
- b = rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
- c = rgbdata2[SKIP + RGB_BYTE0(RGB_ORDER)];
- }
- b = scale8(b, scale);
- c = scale8(c, scale);
-
- while(data < end) {
- // TODO: hand rig asm version of this method. The timings are based on adjusting/studying GCC compiler ouptut. This
- // will bite me in the ass at some point, I know it.
- for(register uint32_t i = 7; i > 0; i--) {
- SET2_HI; delaycycles<3>(); SET2_HI2;
- delaycycles<T1 - 6>(); // 6 cycles - 1/1 store, 1/1 test, 1/1 if, 1/1 port lookup
- if(b & 0x80) { SET2_HI; } else { SET2_LO; }
- if(c & 0x80) { SET2_HI2; } else { SET2_LO2; }
- b <<= 1; c <<=1;
- delaycycles<T2 - 8>(); // 3 cycles, 1/1 store, 1/1 store/skip, 1/1 shift , 1/1 port lookup
- SET2_LO; SET2_LO2;
- delaycycles<T3 - 6>(); // 4 cycles, 1/1 store, 1 sub, 1 branch backwards, 1/1 port lookup
- }
- // extra delay because branch is faster falling through
- delaycycles<1>();
-
- // 8th bit, interleave loading rest of data
- SET2_HI; delaycycles<3>(); SET2_HI2;
- delaycycles<T1 - 6>();
- if(b & 0x80) { SET2_HI; } else { SET2_LO; }
- if(c & 0x80) { SET2_HI2; } else { SET2_LO2; }
- delaycycles<T2 - 6>(); // 4 cycles, 2/2 store, 1/1 store/skip, 1/1 port lookup
- SET2_LO; SET2_LO2;
-
- if(ADVANCE) {
- b = data[SKIP + RGB_BYTE1(RGB_ORDER)];
- c = data2[SKIP + RGB_BYTE1(RGB_ORDER)];
- } else {
- b = rgbdata[SKIP + RGB_BYTE1(RGB_ORDER)];
- c = rgbdata2[SKIP + RGB_BYTE1(RGB_ORDER)];
- }
- b = scale8(b, scale);
- c = scale8(c, scale);
-
- delaycycles<T3 - 12>(); // 1/1 store, 2/2 load, 1/1 mul, 1/1 shift, , 1/1 port lookup
-
- for(register uint32_t i = 7; i > 0; i--) {
- SET2_HI; delaycycles<3>(); SET2_HI2;
- delaycycles<T1 - 6>(); // 3 cycles - 1 store, 1 test, 1 if
- if(b & 0x80) { SET2_HI; } else { SET2_LO; }
- if(c & 0x80) { SET2_HI2; } else { SET2_LO2; }
- b <<= 1; c <<= 1;
- delaycycles<T2 - 8>(); // 6 cycles, 1/1 store, 1/1 store/skip, 1/1 shift , 1/1 port lookup
- SET2_LO; SET2_LO2;
- delaycycles<T3 - 6>(); // 4 cycles, 1/1 store, 1 sub, 1 branch backwards, 1/1 port lookup
- }
- // extra delay because branch is faster falling through
- delaycycles<1>();
-
- // 8th bit, interleave loading rest of data
- SET2_HI; delaycycles<3>(); SET2_HI2;
- delaycycles<T1 - 6>();
- if(b & 0x80) { SET2_HI; } else { SET2_LO; }
- if(b & 0x80) { SET2_HI2; } else { SET2_LO2; }
- delaycycles<T2 - 6>(); // 4 cycles, 2 store, store/skip, 1/1 port lookup
- SET2_LO; SET2_LO2;
-
- if(ADVANCE) {
- b = data[SKIP + RGB_BYTE2(RGB_ORDER)];
- c = data2[SKIP + RGB_BYTE2(RGB_ORDER)];
- } else {
- b = rgbdata[SKIP + RGB_BYTE2(RGB_ORDER)];
- c = rgbdata2[SKIP + RGB_BYTE2(RGB_ORDER)];
- }
- b = scale8(b, scale);
- c = scale8(c, scale);
-
- data += 3 + SKIP; data2 += 3 + SKIP;
- if((RGB_ORDER & 0070) == 0) {
- delaycycles<T3 - 14>(); // 1/1 store, 2/2 load, 1/1 mul, 1/1 shift, 1/1 adds if BRG or GRB, 1/1 port lookup
- } else {
- delaycycles<T3 - 12>(); // 1/1 store, 2/2 load, 1/1 mul, 1/1 shift, , 1/1 port lookup
- }
-
- for(register uint32_t i = 7; i > 0; i--) {
- SET2_HI; delaycycles<3>(); SET2_HI2;
- delaycycles<T1 - 6>(); // 3 cycles - 1 store, 1 test, 1 if
- if(b & 0x80) { SET2_HI; } else { SET2_LO; }
- if(c & 0x80) { SET2_HI2; } else { SET2_LO2; }
- b <<= 1; c <<= 1;
- delaycycles<T2 - 8>(); // 3 cycles, 1 store, 1 store/skip, 1 shift , 1/1 port lookup
- SET2_LO; SET2_LO2;
- delaycycles<T3 - 6>(); // 3 cycles, 1/1 store, 1 sub, 1 branch backwards, 1/1 port lookup
- }
- // extra delay because branch is faster falling through
- delaycycles<1>();
-
- // 8th bit, interleave loading rest of data
- SET2_HI; delaycycles<3>(); SET2_HI2;
- delaycycles<T1 - 6>();
- if(b & 0x80) { SET2_HI; } else { SET2_LO; }
- if(c & 0x80) { SET2_HI2; } else { SET2_LO2; }
- delaycycles<T2 - 6>(); // 4 cycles, 2/2 store, store/skip, 1/1 port lookup
- SET2_LO; SET2_LO2;
-
- if(ADVANCE) {
- b = data[SKIP + RGB_BYTE0(RGB_ORDER)];
- c = data2[SKIP + RGB_BYTE0(RGB_ORDER)];
- } else {
- b = rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
- c = rgbdata2[SKIP + RGB_BYTE0(RGB_ORDER)];
- }
- b = scale8(b, scale);
- c = scale8(c, scale);
- delaycycles<T3 - 15>(); // 1/1 store, 2/2 load (with increment), 1/1 mul, 1/1 shift, 1 cmp, 1 branch backwards, 1 movim, 1/1 port lookup
- };
- }
-
-#ifdef SUPPORT_ARGB
- virtual void showARGB(struct CARGB *data, int nLeds) {
- // TODO: IMPLEMENTME
- }
-#endif
-};
-
-#undef SET2_HI
-#undef SET2_HI2
-#undef SET2_LO
-#undef SET2_LO2
-
-#endif
-
-#endif \ No newline at end of file
diff --git a/clockless_arm_sam.h b/clockless_arm_sam.h
deleted file mode 100644
index 74a0f602..00000000
--- a/clockless_arm_sam.h
+++ /dev/null
@@ -1,217 +0,0 @@
-#ifndef __INC_CLOCKLESS_ARM_SAM_H
-#define __INC_CLOCKLESS_ARM_SAM_H
-
-// Definition for a single channel clockless controller for the sam family of arm chips, like that used in the due and rfduino
-// See clockless.h for detailed info on how the template parameters are used.
-
-#if defined(__SAM3X8E__)
-
-
-#define TADJUST 0
-#define TOTAL ( (T1+TADJUST) + (T2+TADJUST) + (T3+TADJUST) )
-#define T1_MARK (TOTAL - (T1+TADJUST))
-#define T2_MARK (T1_MARK - (T2+TADJUST))
-
-#define SCALE(S,V) scale8_video(S,V)
-// #define SCALE(S,V) scale8(S,V)
-
-template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 500>
-class ClocklessController : public CLEDController {
- typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
- typedef typename FastPinBB<DATA_PIN>::port_t data_t;
-
- data_t mPinMask;
- data_ptr_t mPort;
- CMinWait<WAIT_TIME> mWait;
-public:
- virtual void init() {
- FastPinBB<DATA_PIN>::setOutput();
- mPinMask = FastPinBB<DATA_PIN>::mask();
- mPort = FastPinBB<DATA_PIN>::port();
- }
-
- virtual void clearLeds(int nLeds) {
- showColor(CRGB(0, 0, 0), nLeds, 0);
- }
-
-protected:
-
- // set all the leds on the controller to a given color
- virtual void showColor(const struct CRGB & rgbdata, int nLeds, CRGB scale) {
- PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
- mWait.wait();
- cli();
- SysClockSaver savedClock(TOTAL);
-
- uint32_t clocks = showRGBInternal(pixels);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS(clocks);
- long millisTaken = (microsTaken / 1000);
- savedClock.restore();
- do { TimeTick_Increment(); } while(--millisTaken > 0);
- sei();
- mWait.mark();
- }
-
- virtual void show(const struct CRGB *rgbdata, int nLeds, CRGB scale) {
- PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
- mWait.wait();
- cli();
- SysClockSaver savedClock(TOTAL);
-
- // Serial.print("Scale is ");
- // Serial.print(scale.raw[0]); Serial.print(" ");
- // Serial.print(scale.raw[1]); Serial.print(" ");
- // Serial.print(scale.raw[2]); Serial.println(" ");
- // FastPinBB<DATA_PIN>::hi(); delay(1); FastPinBB<DATA_PIN>::lo();
- uint32_t clocks = showRGBInternal(pixels);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS(clocks);
- long millisTaken = (microsTaken / 1000);
- savedClock.restore();
- do { TimeTick_Increment(); } while(--millisTaken > 0);
- sei();
- mWait.mark();
- }
-
-#ifdef SUPPORT_ARGB
- virtual void show(const struct CARGB *rgbdata, int nLeds, CRGB scale) {
- PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
- mWait.wait();
- cli();
- SysClockSaver savedClock(TOTAL);
-
- uint32_t clocks = showRGBInternal(pixels);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS(clocks);
- long millisTaken = (microsTaken / 1000);
- savedClock.restore();
- do { TimeTick_Increment(); } while(--millisTaken > 0);
- sei();
- mWait.mark();
- }
-#endif
-
-#if 0
-// Get the arm defs, register/macro defs from the k20
-#define ARM_DEMCR *(volatile uint32_t *)0xE000EDFC // Debug Exception and Monitor Control
-#define ARM_DEMCR_TRCENA (1 << 24) // Enable debugging & monitoring blocks
-#define ARM_DWT_CTRL *(volatile uint32_t *)0xE0001000 // DWT control register
-#define ARM_DWT_CTRL_CYCCNTENA (1 << 0) // Enable cycle count
-#define ARM_DWT_CYCCNT *(volatile uint32_t *)0xE0001004 // Cycle count register
-
- template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register uint8_t & b) {
- for(register uint32_t i = BITS; i > 0; i--) {
- while(ARM_DWT_CYCCNT < next_mark);
- next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
- *port = 1;
- uint32_t flip_mark = next_mark - ((b&0x80) ? (T3) : (T2+T3));
- b <<= 1;
- while(ARM_DWT_CYCCNT < flip_mark);
- *port = 0;
- }
- }
-
- // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
- // gcc will use register Y for the this pointer.
- static void showRGBInternal(PixelController<RGB_ORDER> pixels) {
- register data_ptr_t port = FastPinBB<DATA_PIN>::port();
- *port = 0;
-
- // Setup the pixel controller and load/scale the first byte
- pixels.preStepFirstByteDithering();
- register uint8_t b = pixels.loadAndScale0();
-
- // Get access to the clock
- ARM_DEMCR |= ARM_DEMCR_TRCENA;
- ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
- ARM_DWT_CYCCNT = 0;
- uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
-
- while(pixels.has(1)) {
- pixels.stepDithering();
-
- // Write first byte, read next byte
- writeBits<8+XTRA0>(next_mark, port, b);
- b = pixels.loadAndScale1();
-
- // Write second byte, read 3rd byte
- writeBits<8+XTRA0>(next_mark, port, b);
- b = pixels.loadAndScale2();
-
- // Write third byte
- writeBits<8+XTRA0>(next_mark, port, b);
- b = pixels.advanceAndLoadAndScale0();
- };
- }
-#else
-// I hate using defines for these, should find a better representation at some point
-#define _CTRL CTPTR[0]
-#define _LOAD CTPTR[1]
-#define _VAL CTPTR[2]
-#define VAL (volatile uint32_t)(*((uint32_t*)(SysTick_BASE + 8)))
-
- template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register uint8_t & b) {
- for(register uint32_t i = BITS; i > 0; i--) {
- // wait to start the bit, then set the pin high
- while(VAL > next_mark);
- next_mark = (VAL-TOTAL);
- *port = 1;
-
- // how long we want to wait next depends on whether or not our bit is set to 1 or 0
- if(b&0x80) {
- // we're a 1, wait until there's less than T3 clocks left
- while((VAL - next_mark) > (T3));
- } else {
- // we're a 0, wait until there's less than (T2+T3+slop) clocks left in this bit
- while((VAL-next_mark) > (T2+T3+6+TADJUST+TADJUST));
- }
- *port=0;
- b <<= 1;
- }
- }
-
-#define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) )
- // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
- // gcc will use register Y for the this pointer.
- static uint32_t showRGBInternal(PixelController<RGB_ORDER> & pixels) {
- // Setup and start the clock
- register volatile uint32_t *CTPTR asm("r6")= &SysTick->CTRL; FORCE_REFERENCE(CTPTR);
- _LOAD = 0x00FFFFFF;
- _VAL = 0;
- _CTRL |= SysTick_CTRL_CLKSOURCE_Msk;
- _CTRL |= SysTick_CTRL_ENABLE_Msk;
-
- register data_ptr_t port asm("r7") = FastPinBB<DATA_PIN>::port(); FORCE_REFERENCE(port);
- *port = 0;
-
- // Setup the pixel controller and load/scale the first byte
- pixels.preStepFirstByteDithering();
- register uint8_t b = pixels.loadAndScale0();
-
- uint32_t next_mark = (VAL - (TOTAL));
- while(pixels.has(1)) {
- pixels.stepDithering();
-
- writeBits<8+XTRA0>(next_mark, port, b);
-
- b = pixels.loadAndScale1();
- writeBits<8+XTRA0>(next_mark, port,b);
-
- b = pixels.loadAndScale2();
- writeBits<8+XTRA0>(next_mark, port,b);
-
- b = pixels.advanceAndLoadAndScale0();
- };
-
- return 0x00FFFFFF - _VAL;
- }
-#endif
-};
-
-#endif
-
-#endif
diff --git a/clockless_trinket.h b/clockless_trinket.h
deleted file mode 100644
index 63f037f9..00000000
--- a/clockless_trinket.h
+++ /dev/null
@@ -1,366 +0,0 @@
-#ifndef __INC_CLOCKLESS_TRINKET_H
-#define __INC_CLOCKLESS_TRINKET_H
-
-#include "controller.h"
-#include "lib8tion.h"
-#include "delay.h"
-#include <avr/interrupt.h> // for cli/se definitions
-
-#if defined(FASTLED_AVR)
-
-// Scaling macro choice
-#ifndef TRINKET_SCALE
-#define TRINKET_SCALE 1
-// whether or not to use dithering
-#define DITHER 1
-#endif
-
-// Variations on the functions in delay.h - w/a loop var passed in to preserve registers across calls by the optimizer/compiler
-template<int CYCLES> inline void _dc(register uint8_t & loopvar);
-
-template<int _LOOP, int PAD> inline void _dc_AVR(register uint8_t & loopvar) {
- _dc<PAD>(loopvar);
- // The convolution in here is to ensure that the state of the carry flag coming into the delay loop is preserved
- asm __volatile__ ( "BRCS L_PC%=\n\t"
- " LDI %[loopvar], %[_LOOP]\n\tL_%=: DEC %[loopvar]\n\t BRNE L_%=\n\tBREQ L_DONE%=\n\t"
- "L_PC%=: LDI %[loopvar], %[_LOOP]\n\tLL_%=: DEC %[loopvar]\n\t BRNE LL_%=\n\tBSET 0\n\t"
- "L_DONE%=:\n\t"
- :
- [loopvar] "+a" (loopvar) : [_LOOP] "M" (_LOOP) : );
-}
-
-template<int CYCLES> __attribute__((always_inline)) inline void _dc(register uint8_t & loopvar) {
- _dc_AVR<CYCLES/6,CYCLES%6>(loopvar);
-}
-template<> __attribute__((always_inline)) inline void _dc<-6>(register uint8_t & loopvar) {}
-template<> __attribute__((always_inline)) inline void _dc<-5>(register uint8_t & loopvar) {}
-template<> __attribute__((always_inline)) inline void _dc<-4>(register uint8_t & loopvar) {}
-template<> __attribute__((always_inline)) inline void _dc<-3>(register uint8_t & loopvar) {}
-template<> __attribute__((always_inline)) inline void _dc<-2>(register uint8_t & loopvar) {}
-template<> __attribute__((always_inline)) inline void _dc<-1>(register uint8_t & loopvar) {}
-template<> __attribute__((always_inline)) inline void _dc<0>(register uint8_t & loopvar) {}
-template<> __attribute__((always_inline)) inline void _dc<1>(register uint8_t & loopvar) {asm __volatile__("mov r0,r0":::);}
-template<> __attribute__((always_inline)) inline void _dc<2>(register uint8_t & loopvar) {asm __volatile__("rjmp .+0":::);}
-template<> __attribute__((always_inline)) inline void _dc<3>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<1>(loopvar); }
-template<> __attribute__((always_inline)) inline void _dc<4>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<2>(loopvar); }
-template<> __attribute__((always_inline)) inline void _dc<5>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<3>(loopvar); }
-template<> __attribute__((always_inline)) inline void _dc<6>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<2>(loopvar); _dc<2>(loopvar);}
-template<> __attribute__((always_inline)) inline void _dc<7>(register uint8_t & loopvar) { _dc<4>(loopvar); _dc<3>(loopvar); }
-template<> __attribute__((always_inline)) inline void _dc<8>(register uint8_t & loopvar) { _dc<4>(loopvar); _dc<4>(loopvar); }
-template<> __attribute__((always_inline)) inline void _dc<9>(register uint8_t & loopvar) { _dc<5>(loopvar); _dc<4>(loopvar); }
-template<> __attribute__((always_inline)) inline void _dc<10>(register uint8_t & loopvar) { _dc<6>(loopvar); _dc<4>(loopvar); }
-
-#define D1(ADJ) _dc<T1-(AVR_PIN_CYCLES(DATA_PIN)+ADJ)>(loopvar);
-#define D2(ADJ) _dc<T2-(AVR_PIN_CYCLES(DATA_PIN)+ADJ)>(loopvar);
-#define D3(ADJ) _dc<T3-(AVR_PIN_CYCLES(DATA_PIN)+ADJ)>(loopvar);
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. The first point
-// is where the line is raised hi. The second point is where the line is dropped low for a zero. The third point is where the
-// line is dropped low for a one. T1, T2, and T3 correspond to the timings for those three in clock cycles.
-//
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
-class ClocklessController : public CLEDController {
- typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
- typedef typename FastPin<DATA_PIN>::port_t data_t;
-
- CMinWait<WAIT_TIME> mWait;
-public:
- virtual void init() {
- FastPin<DATA_PIN>::setOutput();
- }
-
- virtual void clearLeds(int nLeds) {
- CRGB zeros(0,0,0);
- showAdjTime((uint8_t*)&zeros, nLeds, zeros, false, 0);
- }
-
-protected:
-
- // set all the leds on the controller to a given color
- virtual void showColor(const struct CRGB & rgbdata, int nLeds, CRGB scale) {
- showAdjTime((uint8_t*)&rgbdata, nLeds, scale, false, 0);
- }
-
- virtual void show(const struct CRGB *rgbdata, int nLeds, CRGB scale) {
- showAdjTime((uint8_t*)rgbdata, nLeds, scale, true, 0);
- }
-
-#ifdef SUPPORT_ARGB
- virtual void show(const struct CARGB *rgbdata, int nLeds, CRGB scale) {
- showAdjTime((uint8_t*)rgbdata, nLeds, scale, true, 1);
- }
-#endif
-
- void showAdjTime(const uint8_t *data, int nLeds, CRGB & scale, bool advance, int skip) {
- PixelController<RGB_ORDER> pixels(data, nLeds, scale, getDither(), advance, skip);
-
- mWait.wait();
- cli();
-
- showRGBInternal(pixels);
-
- // Adjust the timer
-#if !defined(NO_CORRECTION) || (NO_CORRECTION == 0)
- uint32_t microsTaken = (uint32_t)nLeds * (uint32_t)CLKS_TO_MICROS(24 * (T1 + T2 + T3));
- if(microsTaken > 1024) {
- MS_COUNTER += (microsTaken >> 10);
- } else {
- MS_COUNTER++;
- }
-#endif
- sei();
- mWait.mark();
- }
-#define USE_ASM_MACROS
-
-// The variables that our various asm statemetns use. The same block of variables needs to be declared for
-// all the asm blocks because GCC is pretty stupid and it would clobber variables happily or optimize code away too aggressively
-#define ASM_VARS : /* write variables */ \
- [count] "+x" (count), \
- [data] "+z" (data), \
- [b0] "+a" (b0), \
- [b1] "+a" (b1), \
- [b2] "+a" (b2), \
- [scale_base] "+a" (scale_base), \
- [loopvar] "+a" (loopvar), \
- [d0] "+r" (d0), \
- [d1] "+r" (d1), \
- [d2] "+r" (d2) \
- : /* use variables */ \
- [ADV] "r" (advanceBy), \
- [hi] "r" (hi), \
- [lo] "r" (lo), \
- [s0] "r" (s0), \
- [s1] "r" (s1), \
- [s2] "r" (s2), \
- [PORT] "M" (FastPin<DATA_PIN>::port()-0x20), \
- [O0] "M" (RGB_BYTE0(RGB_ORDER)), \
- [O1] "M" (RGB_BYTE1(RGB_ORDER)), \
- [O2] "M" (RGB_BYTE2(RGB_ORDER)) \
- : /* clobber registers */
-
-
-// 1 cycle, write hi to the port
-#define HI1 if((int)(FastPin<DATA_PIN>::port())-0x20 < 64) { asm __volatile__("out %[PORT], %[hi]" ASM_VARS ); } else { *FastPin<DATA_PIN>::port()=hi; }
-// 1 cycle, write lo to the port
-#define LO1 if((int)(FastPin<DATA_PIN>::port())-0x20 < 64) { asm __volatile__("out %[PORT], %[lo]" ASM_VARS ); } else { *FastPin<DATA_PIN>::port()=lo; }
-// 2 cycles, sbrs on flipping the lne to lo if we're pushing out a 0
-#define QLO2(B, N) asm __volatile__("sbrs %[" #B "], " #N ASM_VARS ); LO1;
-// load a byte from ram into the given var with the given offset
-#define LD2(B,O) asm __volatile__("ldd %[" #B "], Z + %[" #O "]" ASM_VARS );
-// 4 cycles - load a byte from ram into the scaling scratch space with the given offset, clear the target var, clear carry
-#define LDSCL4(B,O) asm __volatile__("ldd %[scale_base], Z + %[" #O "]\n\tclr %[" #B "]\n\tclc" ASM_VARS );
-// 2 cycles - perform one step of the scaling (if a given bit is set in scale, add scale-base to the scratch space)
-#define SCALE02(B, N) asm __volatile__("sbrc %[s0], " #N "\n\tadd %[" #B "], %[scale_base]" ASM_VARS );
-#define SCALE12(B, N) asm __volatile__("sbrc %[s1], " #N "\n\tadd %[" #B "], %[scale_base]" ASM_VARS );
-#define SCALE22(B, N) asm __volatile__("sbrc %[s2], " #N "\n\tadd %[" #B "], %[scale_base]" ASM_VARS );
-
-// apply dithering value before we do anything with scale_base
-#define PRESCALE4(D) if(DITHER) { asm __volatile__("cpse %[scale_base], __zero_reg__\n\t add %[scale_base],%[" #D "]\n\tbrcc L_%=\n\tldi %[scale_base], 0xFF\n\tL_%=:\n\t" ASM_VARS); } \
- else { _dc<4>(loopvar); }
-
-// Do the add for the prescale
-#define PRESCALEA2(D) if(DITHER) { asm __volatile__("cpse %[scale_base], __zero_reg__\n\t add %[scale_base],%[" #D "]\n\t" ASM_VARS); } \
- else { _dc<2>(loopvar); }
-// Do the clamp for the prescale, clear carry when we're done - NOTE: Must ensure carry flag state is preserved!
-#define PRESCALEB3(D) if(DITHER) { asm __volatile__("brcc L_%=\n\tldi %[scale_base], 0xFF\n\tL_%=:\n\tCLC" ASM_VARS); } \
- else { _dc<3>(loopvar); }
-
-
-// 1 cycle - rotate right, pulling in from carry
-#define ROR1(B) asm __volatile__("ror %[" #B "]" ASM_VARS );
-// 1 cycle, clear the carry bit
-#define CLC1 asm __volatile__("clc" ASM_VARS );
-// 1 cycle, move one register to another
-#define MOV1(B1, B2) asm __volatile__("mov %[" #B1 "], %[" #B2 "]" ASM_VARS );
-// 4 cycles, rotate, clear carry, scale next bit
-#define RORSC04(B, N) ROR1(B) CLC1 SCALE02(B, N)
-#define RORSC14(B, N) ROR1(B) CLC1 SCALE12(B, N)
-#define RORSC24(B, N) ROR1(B) CLC1 SCALE22(B, N)
-// 4 cycles, scale bit, rotate, clear carry
-#define SCROR04(B, N) SCALE02(B,N) ROR1(B) CLC1
-#define SCROR14(B, N) SCALE12(B,N) ROR1(B) CLC1
-#define SCROR24(B, N) SCALE22(B,N) ROR1(B) CLC1
-
-/////////////////////////////////////////////////////////////////////////////////////
-// Loop life cycle
-
-// dither adjustment macro - should be kept in sync w/what's in stepDithering
-#define ADJDITHER2(D, E) D = E - D;
-
-// #define xstr(a) str(a)
-// #define str(a) #a
-// #define ADJDITHER2(D,E) asm __volatile__("subi %[" #D "], " xstr(DUSE) "\n\tand %[" #D "], %[" #E "]\n\t" ASM_VARS);
-
-// define the beginning of the loop
-#define LOOP asm __volatile__("1:" ASM_VARS );
-// define the end of the loop
-#define DONE asm __volatile__("2:" ASM_VARS );
-
-// 2 cycles - increment the data pointer
-#define IDATA2 asm __volatile__("add %A[data], %[ADV]\n\tadc %B[data], __zero_reg__" ASM_VARS );
-// 2 cycles - decrement the counter
-#define DCOUNT2 asm __volatile__("sbiw %[count], 1" ASM_VARS );
-// 2 cycles - jump to the beginning of the loop
-#define JMPLOOP2 asm __volatile__("rjmp 1b" ASM_VARS );
-// 2 cycles - jump out of the loop
-#define BRLOOP1 asm __volatile__("breq 2f" ASM_VARS );
-
-#define DADVANCE 3
-#define DUSE (0xFF - (DADVANCE-1))
-
- // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
- // gcc will use register Y for the this pointer.
- static void __attribute__ ((always_inline)) showRGBInternal(PixelController<RGB_ORDER> & pixels) {
- uint8_t *data = (uint8_t*)pixels.mData;
- data_ptr_t port = FastPin<DATA_PIN>::port();
- data_t mask = FastPin<DATA_PIN>::mask();
- uint8_t scale_base = 0;
-
- // register uint8_t *end = data + nLeds;
- data_t hi = *port | mask;
- data_t lo = *port & ~mask;
- *port = lo;
-
- uint8_t b0 = 0;
- uint8_t b1 = 0;
- uint8_t b2 = 0;
-
- // Setup the pixel controller and load/scale the first byte
- pixels.preStepFirstByteDithering();
- b0 = pixels.loadAndScale0();
-
- // pull the dithering/adjustment values out of the pixels object for direct asm access
-
- uint8_t advanceBy = pixels.advanceBy();
- uint16_t count = pixels.mLen;
-
- uint8_t s0 = pixels.mScale.raw[RO(0)];
- uint8_t s1 = pixels.mScale.raw[RO(1)];
- uint8_t s2 = pixels.mScale.raw[RO(2)];
- uint8_t d0 = pixels.d[RO(0)];
- uint8_t d1 = pixels.d[RO(1)];
- uint8_t d2 = pixels.d[RO(2)];
- uint8_t e0 = pixels.e[RO(0)];
- uint8_t e1 = pixels.e[RO(1)];
- uint8_t e2 = pixels.e[RO(2)];
-
- uint8_t loopvar=0;
-
- {
- while(count--)
- {
- // Loop beginning, does some stuff that's outside of the pixel write cycle, namely incrementing d0-2 and masking off
- // by the E values (see the definition )
- // LOOP;
- ADJDITHER2(d0,e0);
- ADJDITHER2(d1,e1);
- ADJDITHER2(d2,e2);
-
- hi = *port | mask;
- lo = *port & ~mask;
-
- // Sum of the clock counts across each row should be 10 for 8Mhz, WS2811
- // The values in the D1/D2/D3 indicate how many cycles the previous column takes
- // to allow things to line back up.
- //
- // While writing out byte 0, we're loading up byte 1, applying the dithering adjustment,
- // then scaling it using 8 cycles of shift/add interleaved in between writing the bits
- // out. When doing byte 1, we're doing the above for byte 2. When we're doing byte 2,
- // we're cycling back around and doing the above for byte 0.
-#if TRINKET_SCALE
- // Inline scaling - RGB ordering
- HI1 D1(1) QLO2(b0, 7) LDSCL4(b1,O1) D2(4) LO1 PRESCALEA2(d1) D3(2)
- HI1 D1(1) QLO2(b0, 6) PRESCALEB3(d1) D2(3) LO1 SCALE12(b1,0) D3(2)
- HI1 D1(1) QLO2(b0, 5) RORSC14(b1,1) D2(4) LO1 ROR1(b1) CLC1 D3(2)
- HI1 D1(1) QLO2(b0, 4) SCROR14(b1,2) D2(4) LO1 SCALE12(b1,3) D3(2)
- HI1 D1(1) QLO2(b0, 3) RORSC14(b1,4) D2(4) LO1 ROR1(b1) CLC1 D3(2)
- HI1 D1(1) QLO2(b0, 2) SCROR14(b1,5) D2(4) LO1 SCALE12(b1,6) D3(2)
- HI1 D1(1) QLO2(b0, 1) RORSC14(b1,7) D2(4) LO1 ROR1(b1) CLC1 D3(2)
- HI1 D1(1) QLO2(b0, 0) D2(0) LO1 D3(0)
- switch(XTRA0) {
- case 4: HI1 D1(1) QLO2(b0,0) D2(0) LO1 D3(0);
- case 3: HI1 D1(1) QLO2(b0,0) D2(0) LO1 D3(0);
- case 2: HI1 D1(1) QLO2(b0,0) D2(0) LO1 D3(0);
- case 1: HI1 D1(1) QLO2(b0,0) D2(0) LO1 D3(0);
- }
- HI1 D1(1) QLO2(b1, 7) LDSCL4(b2,O2) D2(4) LO1 PRESCALEA2(d2) D3(2)
- HI1 D1(1) QLO2(b1, 6) PRESCALEB3(d2) D2(3) LO1 SCALE22(b2,0) D3(2)
- HI1 D1(1) QLO2(b1, 5) RORSC24(b2,1) D2(4) LO1 ROR1(b2) CLC1 D3(2)
- HI1 D1(1) QLO2(b1, 4) SCROR24(b2,2) D2(4) LO1 SCALE22(b2,3) D3(2)
- HI1 D1(1) QLO2(b1, 3) RORSC24(b2,4) D2(4) LO1 ROR1(b2) CLC1 D3(2)
- HI1 D1(1) QLO2(b1, 2) SCROR24(b2,5) D2(4) LO1 SCALE22(b2,6) D3(2)
- HI1 D1(1) QLO2(b1, 1) RORSC24(b2,7) D2(4) LO1 ROR1(b2) CLC1 D3(2)
- HI1 D1(1) QLO2(b1, 0) IDATA2 CLC1 D2(3) LO1 D3(0)
- switch(XTRA0) {
- case 4: HI1 D1(1) QLO2(b1,0) D2(0) LO1 D3(0);
- case 3: HI1 D1(1) QLO2(b1,0) D2(0) LO1 D3(0);
- case 2: HI1 D1(1) QLO2(b1,0) D2(0) LO1 D3(0);
- case 1: HI1 D1(1) QLO2(b1,0) D2(0) LO1 D3(0);
- }
- HI1 D1(1) QLO2(b2, 7) LDSCL4(b0,O0) D2(4) LO1 PRESCALEA2(d0) D3(2)
- HI1 D1(1) QLO2(b2, 6) PRESCALEB3(d0) D2(3) LO1 SCALE02(b0,0) D3(2)
- HI1 D1(1) QLO2(b2, 5) RORSC04(b0,1) D2(4) LO1 ROR1(b0) CLC1 D3(2)
- HI1 D1(1) QLO2(b2, 4) SCROR04(b0,2) D2(4) LO1 SCALE02(b0,3) D3(2)
- HI1 D1(1) QLO2(b2, 3) RORSC04(b0,4) D2(4) LO1 ROR1(b0) CLC1 D3(2)
- HI1 D1(1) QLO2(b2, 2) SCROR04(b0,5) D2(4) LO1 SCALE02(b0,6) D3(2)
- HI1 D1(1) QLO2(b2, 1) RORSC04(b0,7) D2(4) LO1 ROR1(b0) CLC1 D3(2)
- // HI1 D1(1) QLO2(b2, 0) DCOUNT2 BRLOOP1 D2(3) LO1 D3(2) JMPLOOP2
- HI1 D1(1) QLO2(b2, 0) D2(0) LO1 D3(0)
- switch(XTRA0) {
- case 4: HI1 D1(1) QLO2(b1,0) D2(0) LO1 D3(0);
- case 3: HI1 D1(1) QLO2(b1,0) D2(0) LO1 D3(0);
- case 2: HI1 D1(1) QLO2(b1,0) D2(0) LO1 D3(0);
- case 1: HI1 D1(1) QLO2(b1,0) D2(0) LO1 D3(0);
- }
-#else
- // no inline scaling - non-straight RGB ordering
- HI1 D1(1) QLO2(b0, 7) LD2(b1,O1) D2(2) LO1 D3(0)
- HI1 D1(1) QLO2(b0, 6) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b0, 5) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b0, 4) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b0, 3) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b0, 2) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b0, 1) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b0, 0) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b1, 7) LD2(b2,O2) D2(2) LO1 D3(0)
- HI1 D1(1) QLO2(b1, 6) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b1, 5) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b1, 4) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b1, 3) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b1, 2) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b1, 1) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b1, 0) IDATA2 D2(2) LO1 D3(0)
- HI1 D1(1) QLO2(b2, 7) LD2(b0,O0) D2(2) LO1 D3(0)
- HI1 D1(1) QLO2(b2, 6) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b2, 5) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b2, 4) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b2, 3) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b2, 2) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b2, 1) D2(0) LO1 D3(0)
- HI1 D1(1) QLO2(b2, 0) D2(0) LO1 D3(0)
-#endif
- // DONE
- // D2(4) LO1 D3(0)
- }
- }
- // save the d values
- // d[0] = d0;
- // d[1] = d1;
- // d[2] = d2;
- }
-
-#ifdef SUPPORT_ARGB
- virtual void showARGB(struct CARGB *data, int nLeds) {
- // TODO: IMPLEMENTME
- }
-#endif
-};
-
-#endif
-
-#endif
diff --git a/color.h b/color.h
index 17d1b20a..11b52d36 100644
--- a/color.h
+++ b/color.h
@@ -1,42 +1,65 @@
#ifndef __INC_COLOR_H
#define __INC_COLOR_H
-// definitions for color correction and light temperatures
+FASTLED_NAMESPACE_BEGIN
+///@file color.h
+/// contains definitions for color correction and temperature
+///@defgroup ColorEnums Color correction/temperature
+/// definitions for color correction and light temperatures
+///@{
typedef enum {
// Color correction starting points
- // typical values for SMD5050 LEDs
+ /// typical values for SMD5050 LEDs
+ ///@{
TypicalSMD5050=0xFFB0F0 /* 255, 176, 240 */,
TypicalLEDStrip=0xFFB0F0 /* 255, 176, 240 */,
+ ///@}
- // typical values for 8mm "pixels on a string"
- // also for many through-hole 'T' package LEDs
+ /// typical values for 8mm "pixels on a string"
+ /// also for many through-hole 'T' package LEDs
+ ///@{
Typical8mmPixel=0xFFE08C /* 255, 224, 140 */,
TypicalPixelString=0xFFE08C /* 255, 224, 140 */,
-
- // uncorrected color
+ ///@}
+
+ /// uncorrected color
UncorrectedColor=0xFFFFFF
-
+
} LEDColorCorrection;
typedef enum {
- // Black-body radiation light sources emit a (relatively) continuous
- // spectrum, and can be described as having a Kelvin 'temperature'
+ /// @name Black-body radiation light sources
+ /// Black-body radiation light sources emit a (relatively) continuous
+ /// spectrum, and can be described as having a Kelvin 'temperature'
+ ///@{
+ /// 1900 Kelvin
Candle=0xFF9329 /* 1900 K, 255, 147, 41 */,
+ /// 2600 Kelvin
Tungsten40W=0xFFC58F /* 2600 K, 255, 197, 143 */,
+ /// 2850 Kelvin
Tungsten100W=0xFFD6AA /* 2850 K, 255, 214, 170 */,
+ /// 3200 Kelvin
Halogen=0xFFF1E0 /* 3200 K, 255, 241, 224 */,
+ /// 5200 Kelvin
CarbonArc=0xFFFAF4 /* 5200 K, 255, 250, 244 */,
+ /// 5400 Kelvin
HighNoonSun=0xFFFFFB /* 5400 K, 255, 255, 251 */,
+ /// 6000 Kelvin
DirectSunlight=0xFFFFFF /* 6000 K, 255, 255, 255 */,
+ /// 7000 Kelvin
OvercastSky=0xC9E2FF /* 7000 K, 201, 226, 255 */,
+ /// 20000 Kelvin
ClearBlueSky=0x409CFF /* 20000 K, 64, 156, 255 */,
+ ///@}
- // Gaseous light sources emit discrete spectral bands, and while we can
- // approximate their aggregate hue with RGB values, they don't actually
- // have a proper Kelvin temperature.
+ /// @name Gaseous light sources
+ /// Gaseous light sources emit discrete spectral bands, and while we can
+ /// approximate their aggregate hue with RGB values, they don't actually
+ /// have a proper Kelvin temperature.
+ ///@{
WarmFluorescent=0xFFF4E5 /* 0 K, 255, 244, 229 */,
StandardFluorescent=0xF4FFFA /* 0 K, 244, 255, 250 */,
CoolWhiteFluorescent=0xD4EBFF /* 0 K, 212, 235, 255 */,
@@ -47,8 +70,13 @@ typedef enum {
SodiumVapor=0xFFD1B2 /* 0 K, 255, 209, 178 */,
MetalHalide=0xF2FCFF /* 0 K, 242, 252, 255 */,
HighPressureSodium=0xFFB74C /* 0 K, 255, 183, 76 */,
+ ///@}
- // Uncorrected temperature 0xFFFFFF
+ /// Uncorrected temperature 0xFFFFFF
UncorrectedTemperature=0xFFFFFF
} ColorTemperature;
-#endif \ No newline at end of file
+
+FASTLED_NAMESPACE_END
+
+///@}
+#endif
diff --git a/colorpalettes.cpp b/colorpalettes.cpp
index 8cec8980..3c3a1f51 100644
--- a/colorpalettes.cpp
+++ b/colorpalettes.cpp
@@ -1,18 +1,11 @@
#ifndef __INC_COLORPALETTES_H
#define __INC_COLORPALETTES_H
-
+#define FASTLED_INTERNAL
#include "FastLED.h"
#include "colorutils.h"
#include "colorpalettes.h"
-// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734
-#ifdef FASTLED_AVR
-#ifdef PROGMEM
-#undef PROGMEM
-#define PROGMEM __attribute__((section(".progmem.data")))
-#endif
-#endif
-
+FASTLED_USING_NAMESPACE
// Preset color schemes, such as they are.
@@ -24,45 +17,45 @@
// use each one, so you only 'pay for' those you actually use.
-extern const TProgmemRGBPalette16 CloudColors_p PROGMEM =
+extern const TProgmemRGBPalette16 CloudColors_p FL_PROGMEM =
{
CRGB::Blue,
CRGB::DarkBlue,
CRGB::DarkBlue,
CRGB::DarkBlue,
-
+
CRGB::DarkBlue,
CRGB::DarkBlue,
CRGB::DarkBlue,
CRGB::DarkBlue,
-
+
CRGB::Blue,
CRGB::DarkBlue,
CRGB::SkyBlue,
CRGB::SkyBlue,
-
+
CRGB::LightBlue,
CRGB::White,
CRGB::LightBlue,
CRGB::SkyBlue
};
-extern const TProgmemRGBPalette16 LavaColors_p PROGMEM =
+extern const TProgmemRGBPalette16 LavaColors_p FL_PROGMEM =
{
CRGB::Black,
CRGB::Maroon,
CRGB::Black,
CRGB::Maroon,
-
+
CRGB::DarkRed,
CRGB::Maroon,
CRGB::DarkRed,
-
+
CRGB::DarkRed,
CRGB::DarkRed,
CRGB::Red,
CRGB::Orange,
-
+
CRGB::White,
CRGB::Orange,
CRGB::Red,
@@ -70,54 +63,54 @@ extern const TProgmemRGBPalette16 LavaColors_p PROGMEM =
};
-extern const TProgmemRGBPalette16 OceanColors_p PROGMEM =
+extern const TProgmemRGBPalette16 OceanColors_p FL_PROGMEM =
{
CRGB::MidnightBlue,
CRGB::DarkBlue,
CRGB::MidnightBlue,
CRGB::Navy,
-
+
CRGB::DarkBlue,
CRGB::MediumBlue,
CRGB::SeaGreen,
CRGB::Teal,
-
+
CRGB::CadetBlue,
CRGB::Blue,
CRGB::DarkCyan,
CRGB::CornflowerBlue,
-
+
CRGB::Aquamarine,
CRGB::SeaGreen,
CRGB::Aqua,
CRGB::LightSkyBlue
};
-extern const TProgmemRGBPalette16 ForestColors_p PROGMEM =
+extern const TProgmemRGBPalette16 ForestColors_p FL_PROGMEM =
{
CRGB::DarkGreen,
CRGB::DarkGreen,
CRGB::DarkOliveGreen,
CRGB::DarkGreen,
-
+
CRGB::Green,
CRGB::ForestGreen,
CRGB::OliveDrab,
CRGB::Green,
-
+
CRGB::SeaGreen,
CRGB::MediumAquamarine,
CRGB::LimeGreen,
CRGB::YellowGreen,
-
+
CRGB::LightGreen,
CRGB::LawnGreen,
CRGB::MediumAquamarine,
CRGB::ForestGreen
};
-// HSV Rainbow
-extern const TProgmemRGBPalette16 RainbowColors_p PROGMEM =
+/// HSV Rainbow
+extern const TProgmemRGBPalette16 RainbowColors_p FL_PROGMEM =
{
0xFF0000, 0xD52A00, 0xAB5500, 0xAB7F00,
0xABAB00, 0x56D500, 0x00FF00, 0x00D52A,
@@ -125,9 +118,9 @@ extern const TProgmemRGBPalette16 RainbowColors_p PROGMEM =
0x5500AB, 0x7F0081, 0xAB0055, 0xD5002B
};
-// HSV Rainbow colors with alternatating stripes of black
+/// HSV Rainbow colors with alternatating stripes of black
#define RainbowStripesColors_p RainbowStripeColors_p
-extern const TProgmemRGBPalette16 RainbowStripeColors_p PROGMEM =
+extern const TProgmemRGBPalette16 RainbowStripeColors_p FL_PROGMEM =
{
0xFF0000, 0x000000, 0xAB5500, 0x000000,
0xABAB00, 0x000000, 0x00FF00, 0x000000,
@@ -135,11 +128,11 @@ extern const TProgmemRGBPalette16 RainbowStripeColors_p PROGMEM =
0x5500AB, 0x000000, 0xAB0055, 0x000000
};
-// HSV color ramp: blue purple ping red orange yellow (and back)
-// Basically, everything but the greens, which tend to make
-// people's skin look unhealthy. This palette is good for
-// lighting at a club or party, where it'll be shining on people.
-extern const TProgmemRGBPalette16 PartyColors_p PROGMEM =
+/// HSV color ramp: blue purple ping red orange yellow (and back)
+/// Basically, everything but the greens, which tend to make
+/// people's skin look unhealthy. This palette is good for
+/// lighting at a club or party, where it'll be shining on people.
+extern const TProgmemRGBPalette16 PartyColors_p FL_PROGMEM =
{
0x5500AB, 0x84007C, 0xB5004B, 0xE5001B,
0xE81700, 0xB84700, 0xAB7700, 0xABAB00,
@@ -147,18 +140,35 @@ extern const TProgmemRGBPalette16 PartyColors_p PROGMEM =
0x8F0071, 0x5F00A1, 0x2F00D0, 0x0007F9
};
-// Approximate "black body radiation" palette, akin to
-// the FastLED 'HeatColor' function.
-// Recommend that you use values 0-240 rather than
-// the usual 0-255, as the last 15 colors will be
-// 'wrapping around' from the hot end to the cold end,
-// which looks wrong.
-extern const TProgmemRGBPalette16 HeatColors_p PROGMEM =
+/// Approximate "black body radiation" palette, akin to
+/// the FastLED 'HeatColor' function.
+/// Recommend that you use values 0-240 rather than
+/// the usual 0-255, as the last 15 colors will be
+/// 'wrapping around' from the hot end to the cold end,
+/// which looks wrong.
+extern const TProgmemRGBPalette16 HeatColors_p FL_PROGMEM =
{
0x000000,
0x330000, 0x660000, 0x990000, 0xCC0000, 0xFF0000,
0xFF3300, 0xFF6600, 0xFF9900, 0xFFCC00, 0xFFFF00,
0xFFFF33, 0xFFFF66, 0xFFFF99, 0xFFFFCC, 0xFFFFFF
};
-
+
+
+// Gradient palette "Rainbow_gp",
+// provided for situations where you're going
+// to use a number of other gradient palettes, AND
+// you want a 'standard' FastLED rainbow as well.
+
+DEFINE_GRADIENT_PALETTE( Rainbow_gp ) {
+ 0, 255, 0, 0, // Red
+ 32, 171, 85, 0, // Orange
+ 64, 171,171, 0, // Yellow
+ 96, 0,255, 0, // Green
+ 128, 0,171, 85, // Aqua
+ 160, 0, 0,255, // Blue
+ 192, 85, 0,171, // Purple
+ 224, 171, 0, 85, // Pink
+ 255, 255, 0, 0};// and back to Red
+
#endif
diff --git a/colorpalettes.h b/colorpalettes.h
index 340349d4..97f0cb5d 100644
--- a/colorpalettes.h
+++ b/colorpalettes.h
@@ -3,40 +3,54 @@
#include "colorutils.h"
+///@file colorpalettes.h
+/// contains definitions for the predefined color palettes supplied by FastLED.
-// Preset color schemes, such as they are.
+FASTLED_NAMESPACE_BEGIN
-// These schemes are all declared as "PROGMEM", meaning
-// that they won't take up SRAM on AVR chips until used.
-// Furthermore, the compiler won't even include these
-// in your PROGMEM (flash) storage unless you specifically
-// use each one, so you only 'pay for' those you actually use.
+///@defgroup Colorpalletes Pre-defined color palletes
+/// These schemes are all declared as "PROGMEM", meaning
+/// that they won't take up SRAM on AVR chips until used.
+/// Furthermore, the compiler won't even include these
+/// in your PROGMEM (flash) storage unless you specifically
+/// use each one, so you only 'pay for' those you actually use.
+///@{
-extern const TProgmemRGBPalette16 CloudColors_p PROGMEM;
-extern const TProgmemRGBPalette16 LavaColors_p PROGMEM;
-extern const TProgmemRGBPalette16 OceanColors_p PROGMEM;
-extern const TProgmemRGBPalette16 ForestColors_p PROGMEM;
+/// Cloudy color pallete
+extern const TProgmemRGBPalette16 CloudColors_p FL_PROGMEM;
+/// Lava colors
+extern const TProgmemRGBPalette16 LavaColors_p FL_PROGMEM;
+/// Ocean colors, blues and whites
+extern const TProgmemRGBPalette16 OceanColors_p FL_PROGMEM;
+/// Forest colors, greens
+extern const TProgmemRGBPalette16 ForestColors_p FL_PROGMEM;
-// HSV Rainbow
-extern const TProgmemRGBPalette16 RainbowColors_p PROGMEM;
+/// HSV Rainbow
+extern const TProgmemRGBPalette16 RainbowColors_p FL_PROGMEM;
-// HSV Rainbow colors with alternatating stripes of black
#define RainbowStripesColors_p RainbowStripeColors_p
-extern const TProgmemRGBPalette16 RainbowStripeColors_p PROGMEM;
-
-// HSV color ramp: blue purple ping red orange yellow (and back)
-// Basically, everything but the greens, which tend to make
-// people's skin look unhealthy. This palette is good for
-// lighting at a club or party, where it'll be shining on people.
-extern const TProgmemRGBPalette16 PartyColors_p PROGMEM;
-
-// Approximate "black body radiation" palette, akin to
-// the FastLED 'HeatColor' function.
-// Recommend that you use values 0-240 rather than
-// the usual 0-255, as the last 15 colors will be
-// 'wrapping around' from the hot end to the cold end,
-// which looks wrong.
-extern const TProgmemRGBPalette16 HeatColors_p PROGMEM;
-
+/// HSV Rainbow colors with alternatating stripes of black
+extern const TProgmemRGBPalette16 RainbowStripeColors_p FL_PROGMEM;
+
+/// HSV color ramp: blue purple ping red orange yellow (and back)
+/// Basically, everything but the greens, which tend to make
+/// people's skin look unhealthy. This palette is good for
+/// lighting at a club or party, where it'll be shining on people.
+extern const TProgmemRGBPalette16 PartyColors_p FL_PROGMEM;
+
+/// Approximate "black body radiation" palette, akin to
+/// the FastLED 'HeatColor' function.
+/// Recommend that you use values 0-240 rather than
+/// the usual 0-255, as the last 15 colors will be
+/// 'wrapping around' from the hot end to the cold end,
+/// which looks wrong.
+extern const TProgmemRGBPalette16 HeatColors_p FL_PROGMEM;
+
+
+DECLARE_GRADIENT_PALETTE( Rainbow_gp);
+
+FASTLED_NAMESPACE_END
+
+///@}
#endif
diff --git a/colorutils.cpp b/colorutils.cpp
index 995db69e..5decca2e 100644
--- a/colorutils.cpp
+++ b/colorutils.cpp
@@ -1,10 +1,11 @@
+#define FASTLED_INTERNAL
#define __PROG_TYPES_COMPAT__
#include <stdint.h>
-#include "hsv2rgb.h"
-#include "colorutils.h"
+#include "FastLED.h"
+FASTLED_NAMESPACE_BEGIN
@@ -38,9 +39,9 @@ void fill_rainbow( struct CRGB * pFirstLED, int numToFill,
CHSV hsv;
hsv.hue = initialhue;
hsv.val = 255;
- hsv.sat = 255;
+ hsv.sat = 240;
for( int i = 0; i < numToFill; i++) {
- hsv2rgb_rainbow( hsv, pFirstLED[i]);
+ pFirstLED[i] = hsv;
hsv.hue += deltahue;
}
}
@@ -52,7 +53,7 @@ void fill_rainbow( struct CHSV * targetArray, int numToFill,
CHSV hsv;
hsv.hue = initialhue;
hsv.val = 255;
- hsv.sat = 255;
+ hsv.sat = 240;
for( int i = 0; i < numToFill; i++) {
targetArray[i] = hsv;
hsv.hue += deltahue;
@@ -68,27 +69,27 @@ void fill_gradient_RGB( CRGB* leds,
if( endpos < startpos ) {
uint16_t t = endpos;
CRGB tc = endcolor;
- startpos = t;
- startcolor = tc;
endcolor = startcolor;
endpos = startpos;
+ startpos = t;
+ startcolor = tc;
}
-
+
saccum87 rdistance87;
saccum87 gdistance87;
saccum87 bdistance87;
-
+
rdistance87 = (endcolor.r - startcolor.r) << 7;
gdistance87 = (endcolor.g - startcolor.g) << 7;
bdistance87 = (endcolor.b - startcolor.b) << 7;
-
+
uint16_t pixeldistance = endpos - startpos;
int16_t divisor = pixeldistance ? pixeldistance : 1;
saccum87 rdelta87 = rdistance87 / divisor;
saccum87 gdelta87 = gdistance87 / divisor;
saccum87 bdelta87 = bdistance87 / divisor;
-
+
rdelta87 *= 2;
gdelta87 *= 2;
bdelta87 *= 2;
@@ -207,29 +208,43 @@ void nscale8( CRGB* leds, uint16_t num_leds, uint8_t scale)
}
}
+void fadeUsingColor( CRGB* leds, uint16_t numLeds, const CRGB& colormask)
+{
+ uint8_t fr, fg, fb;
+ fr = colormask.r;
+ fg = colormask.g;
+ fb = colormask.b;
+
+ for( uint16_t i = 0; i < numLeds; i++) {
+ leds[i].r = scale8_LEAVING_R1_DIRTY( leds[i].r, fr);
+ leds[i].g = scale8_LEAVING_R1_DIRTY( leds[i].g, fg);
+ leds[i].b = scale8 ( leds[i].b, fb);
+ }
+}
+
CRGB& nblend( CRGB& existing, const CRGB& overlay, fract8 amountOfOverlay )
{
if( amountOfOverlay == 0) {
return existing;
}
-
+
if( amountOfOverlay == 255) {
existing = overlay;
return existing;
}
-
+
fract8 amountOfKeep = 256 - amountOfOverlay;
-
+
existing.red = scale8_LEAVING_R1_DIRTY( existing.red, amountOfKeep)
+ scale8_LEAVING_R1_DIRTY( overlay.red, amountOfOverlay);
existing.green = scale8_LEAVING_R1_DIRTY( existing.green, amountOfKeep)
+ scale8_LEAVING_R1_DIRTY( overlay.green, amountOfOverlay);
existing.blue = scale8_LEAVING_R1_DIRTY( existing.blue, amountOfKeep)
+ scale8_LEAVING_R1_DIRTY( overlay.blue, amountOfOverlay);
-
+
cleanup_R1();
-
+
return existing;
}
@@ -253,7 +268,7 @@ CRGB blend( const CRGB& p1, const CRGB& p2, fract8 amountOfP2 )
CRGB* blend( const CRGB* src1, const CRGB* src2, CRGB* dest, uint16_t count, fract8 amountOfsrc2 )
{
- for( uint16_t i = count; i; i--) {
+ for( uint16_t i = 0; i < count; i++) {
dest[i] = blend(src1[i], src2[i], amountOfsrc2);
}
return dest;
@@ -266,30 +281,30 @@ CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay, TGrad
if( amountOfOverlay == 0) {
return existing;
}
-
+
if( amountOfOverlay == 255) {
existing = overlay;
return existing;
}
-
+
fract8 amountOfKeep = 256 - amountOfOverlay;
-
+
uint8_t huedelta8 = overlay.hue - existing.hue;
-
+
if( directionCode == SHORTEST_HUES ) {
directionCode = FORWARD_HUES;
if( huedelta8 > 127) {
directionCode = BACKWARD_HUES;
}
}
-
+
if( directionCode == LONGEST_HUES ) {
directionCode = FORWARD_HUES;
if( huedelta8 < 128) {
directionCode = BACKWARD_HUES;
}
}
-
+
if( directionCode == FORWARD_HUES) {
existing.hue = existing.hue + scale8( huedelta8, amountOfOverlay);
}
@@ -298,14 +313,14 @@ CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay, TGrad
huedelta8 = -huedelta8;
existing.hue = existing.hue - scale8( huedelta8, amountOfOverlay);
}
-
+
existing.sat = scale8_LEAVING_R1_DIRTY( existing.sat, amountOfKeep)
+ scale8_LEAVING_R1_DIRTY( overlay.sat, amountOfOverlay);
existing.val = scale8_LEAVING_R1_DIRTY( existing.val, amountOfKeep)
+ scale8_LEAVING_R1_DIRTY( overlay.val, amountOfOverlay);
-
+
cleanup_R1();
-
+
return existing;
}
@@ -313,6 +328,7 @@ CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay, TGrad
void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOverlay, TGradientDirectionCode directionCode )
{
+ if(existing == overlay) return;
for( uint16_t i = count; i; i--) {
nblend( *existing, *overlay, amountOfOverlay, directionCode);
existing++;
@@ -329,7 +345,7 @@ CHSV blend( const CHSV& p1, const CHSV& p2, fract8 amountOfP2, TGradientDirectio
CHSV* blend( const CHSV* src1, const CHSV* src2, CHSV* dest, uint16_t count, fract8 amountOfsrc2, TGradientDirectionCode directionCode )
{
- for( uint16_t i = count; i; i--) {
+ for( uint16_t i = 0; i < count; i++) {
dest[i] = blend(src1[i], src2[i], amountOfsrc2, directionCode);
}
return dest;
@@ -337,6 +353,79 @@ CHSV* blend( const CHSV* src1, const CHSV* src2, CHSV* dest, uint16_t count, fra
+// Forward declaration of the function "XY" which must be provided by
+// the application for use in two-dimensional filter functions.
+uint16_t XY( uint8_t, uint8_t);// __attribute__ ((weak));
+
+
+// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors.
+// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors.
+//
+// 0 = no spread at all
+// 64 = moderate spreading
+// 172 = maximum smooth, even spreading
+//
+// 173..255 = wider spreading, but increasing flicker
+//
+// Total light is NOT entirely conserved, so many repeated
+// calls to 'blur' will also result in the light fading,
+// eventually all the way to black; this is by design so that
+// it can be used to (slowly) clear the LEDs to black.
+void blur1d( CRGB* leds, uint16_t numLeds, fract8 blur_amount)
+{
+ uint8_t keep = 255 - blur_amount;
+ uint8_t seep = blur_amount >> 1;
+ CRGB carryover = CRGB::Black;
+ for( uint16_t i = 0; i < numLeds; i++) {
+ CRGB cur = leds[i];
+ CRGB part = cur;
+ part.nscale8( seep);
+ cur.nscale8( keep);
+ cur += carryover;
+ if( i) leds[i-1] += part;
+ leds[i] = cur;
+ carryover = part;
+ }
+}
+
+void blur2d( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount)
+{
+ blurRows(leds, width, height, blur_amount);
+ blurColumns(leds, width, height, blur_amount);
+}
+
+// blurRows: perform a blur1d on every row of a rectangular matrix
+void blurRows( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount)
+{
+ for( uint8_t row = 0; row < height; row++) {
+ CRGB* rowbase = leds + (row * width);
+ blur1d( rowbase, width, blur_amount);
+ }
+}
+
+// blurColumns: perform a blur1d on each column of a rectangular matrix
+void blurColumns(CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount)
+{
+ // blur columns
+ uint8_t keep = 255 - blur_amount;
+ uint8_t seep = blur_amount >> 1;
+ for( uint8_t col = 0; col < width; col++) {
+ CRGB carryover = CRGB::Black;
+ for( uint8_t i = 0; i < height; i++) {
+ CRGB cur = leds[XY(col,i)];
+ CRGB part = cur;
+ part.nscale8( seep);
+ cur.nscale8( keep);
+ cur += carryover;
+ if( i) leds[XY(col,i-1)] += part;
+ leds[XY(col,i)] = cur;
+ carryover = part;
+ }
+ }
+}
+
+
+
// CRGB HeatColor( uint8_t temperature)
//
// Approximates a 'black body radiation' spectrum for
@@ -351,37 +440,37 @@ CHSV* blend( const CHSV* src1, const CHSV* src2, CHSV* dest, uint16_t count, fra
CRGB HeatColor( uint8_t temperature)
{
CRGB heatcolor;
-
+
// Scale 'heat' down from 0-255 to 0-191,
// which can then be easily divided into three
// equal 'thirds' of 64 units each.
uint8_t t192 = scale8_video( temperature, 192);
-
+
// calculate a value that ramps up from
// zero to 255 in each 'third' of the scale.
uint8_t heatramp = t192 & 0x3F; // 0..63
heatramp <<= 2; // scale up to 0..252
-
+
// now figure out which third of the spectrum we're in:
if( t192 & 0x80) {
// we're in the hottest third
heatcolor.r = 255; // full red
heatcolor.g = 255; // full green
heatcolor.b = heatramp; // ramp up blue
-
+
} else if( t192 & 0x40 ) {
// we're in the middle third
heatcolor.r = 255; // full red
heatcolor.g = heatramp; // ramp up green
heatcolor.b = 0; // no blue
-
+
} else {
// we're in the coolest third
heatcolor.r = heatramp; // ramp up red
heatcolor.g = 0; // no green
heatcolor.b = 0; // no blue
}
-
+
return heatcolor;
}
@@ -391,33 +480,33 @@ CRGB ColorFromPalette( const CRGBPalette16& pal, uint8_t index, uint8_t brightne
{
uint8_t hi4 = index >> 4;
uint8_t lo4 = index & 0x0F;
-
+
// CRGB rgb1 = pal[ hi4];
const CRGB* entry = &(pal[0]) + hi4;
uint8_t red1 = entry->red;
uint8_t green1 = entry->green;
uint8_t blue1 = entry->blue;
-
+
uint8_t blend = lo4 && (blendType != NOBLEND);
-
+
if( blend ) {
-
+
if( hi4 == 15 ) {
entry = &(pal[0]);
} else {
entry++;
}
-
+
uint8_t f2 = lo4 << 4;
uint8_t f1 = 256 - f2;
-
+
// rgb1.nscale8(f1);
red1 = scale8_LEAVING_R1_DIRTY( red1, f1);
green1 = scale8_LEAVING_R1_DIRTY( green1, f1);
blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1);
-
+
// cleanup_R1();
-
+
// CRGB rgb2 = pal[ hi4];
// rgb2.nscale8(f2);
uint8_t red2 = entry->red;
@@ -426,23 +515,81 @@ CRGB ColorFromPalette( const CRGBPalette16& pal, uint8_t index, uint8_t brightne
red2 = scale8_LEAVING_R1_DIRTY( red2, f2);
green2 = scale8_LEAVING_R1_DIRTY( green2, f2);
blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2);
-
+
cleanup_R1();
-
+
// These sums can't overflow, so no qadd8 needed.
red1 += red2;
green1 += green2;
blue1 += blue2;
}
-
+
if( brightness != 255) {
nscale8x3_video( red1, green1, blue1, brightness);
}
-
- return CRGB( red1, green1, blue1);
+
+ return CRGB( red1, green1, blue1);
}
+CRGB ColorFromPalette( const TProgmemRGBPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType)
+{
+ uint8_t hi4 = index >> 4;
+ uint8_t lo4 = index & 0x0F;
+
+ // CRGB rgb1 = pal[ hi4];
+ CRGB entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) + hi4 );
+
+ uint8_t red1 = entry.red;
+ uint8_t green1 = entry.green;
+ uint8_t blue1 = entry.blue;
+
+ uint8_t blend = lo4 && (blendType != NOBLEND);
+
+ if( blend ) {
+
+ if( hi4 == 15 ) {
+ entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) );
+ } else {
+ entry = FL_PGM_READ_DWORD_NEAR( &(pal[1]) + hi4 );
+ }
+
+ uint8_t f2 = lo4 << 4;
+ uint8_t f1 = 256 - f2;
+
+ // rgb1.nscale8(f1);
+ red1 = scale8_LEAVING_R1_DIRTY( red1, f1);
+ green1 = scale8_LEAVING_R1_DIRTY( green1, f1);
+ blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1);
+
+ // cleanup_R1();
+
+ // CRGB rgb2 = pal[ hi4];
+ // rgb2.nscale8(f2);
+ uint8_t red2 = entry.red;
+ uint8_t green2 = entry.green;
+ uint8_t blue2 = entry.blue;
+ red2 = scale8_LEAVING_R1_DIRTY( red2, f2);
+ green2 = scale8_LEAVING_R1_DIRTY( green2, f2);
+ blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2);
+
+ cleanup_R1();
+
+ // These sums can't overflow, so no qadd8 needed.
+ red1 += red2;
+ green1 += green2;
+ blue1 += blue2;
+
+ }
+
+ if( brightness != 255) {
+ nscale8x3_video( red1, green1, blue1, brightness);
+ }
+
+ return CRGB( red1, green1, blue1);
+}
+
+
CRGB ColorFromPalette( const CRGBPalette256& pal, uint8_t index, uint8_t brightness, TBlendType)
{
@@ -451,11 +598,11 @@ CRGB ColorFromPalette( const CRGBPalette256& pal, uint8_t index, uint8_t brightn
uint8_t red = entry->red;
uint8_t green = entry->green;
uint8_t blue = entry->blue;
-
+
if( brightness != 255) {
nscale8x3_video( red, green, blue, brightness);
}
-
+
return CRGB( red, green, blue);
}
@@ -464,27 +611,27 @@ CHSV ColorFromPalette( const struct CHSVPalette16& pal, uint8_t index, uint8_t b
{
uint8_t hi4 = index >> 4;
uint8_t lo4 = index & 0x0F;
-
+
// CRGB rgb1 = pal[ hi4];
const CHSV* entry = &(pal[0]) + hi4;
uint8_t hue1 = entry->hue;
uint8_t sat1 = entry->sat;
uint8_t val1 = entry->val;
-
+
uint8_t blend = lo4 && (blendType != NOBLEND);
-
+
if( blend ) {
-
+
if( hi4 == 15 ) {
entry = &(pal[0]);
} else {
entry++;
}
-
+
uint8_t f2 = lo4 << 4;
uint8_t f1 = 256 - f2;
-
+
uint8_t hue2 = entry->hue;
uint8_t sat2 = entry->sat;
uint8_t val2 = entry->val;
@@ -497,26 +644,26 @@ CHSV ColorFromPalette( const struct CHSVPalette16& pal, uint8_t index, uint8_t b
// of the other color, so that you get the expected
// brightness or saturation ramp, with hue staying
// constant:
-
+
// If we are starting from white (sat=0)
// or black (val=0), adopt the target hue.
if( sat1 == 0 || val1 == 0) {
hue1 = hue2;
}
-
+
// If we are ending at white (sat=0)
// or black (val=0), adopt the starting hue.
if( sat2 == 0 || val2 == 0) {
hue2 = hue1;
}
-
-
+
+
sat1 = scale8_LEAVING_R1_DIRTY( sat1, f1);
val1 = scale8_LEAVING_R1_DIRTY( val1, f1);
-
+
sat2 = scale8_LEAVING_R1_DIRTY( sat2, f2);
val2 = scale8_LEAVING_R1_DIRTY( val2, f2);
-
+
// cleanup_R1();
// These sums can't overflow, so no qadd8 needed.
@@ -531,15 +678,15 @@ CHSV ColorFromPalette( const struct CHSVPalette16& pal, uint8_t index, uint8_t b
// go forwards
hue1 += scale8( deltaHue, f2);
}
-
+
cleanup_R1();
}
-
+
if( brightness != 255) {
val1 = scale8_video( val1, brightness);
}
-
- return CHSV( hue1, sat1, val1);
+
+ return CHSV( hue1, sat1, val1);
}
@@ -550,7 +697,7 @@ CHSV ColorFromPalette( const struct CHSVPalette256& pal, uint8_t index, uint8_t
if( brightness != 255) {
hsv.value = scale8_video( hsv.value, brightness);
}
-
+
return hsv;
}
@@ -577,3 +724,94 @@ void SetupPartyColors(CRGBPalette16& pal)
fill_gradient( pal, 8, CHSV( HUE_ORANGE,255,255), 15, CHSV(HUE_BLUE + 18,255,255), BACKWARD_HUES);
}
#endif
+
+
+void nblendPaletteTowardPalette( CRGBPalette16& current, CRGBPalette16& target, uint8_t maxChanges)
+{
+ uint8_t* p1;
+ uint8_t* p2;
+ uint8_t changes = 0;
+
+ p1 = (uint8_t*)current.entries;
+ p2 = (uint8_t*)target.entries;
+
+ const uint8_t totalChannels = sizeof(CRGBPalette16);
+ for( uint8_t i = 0; i < totalChannels; i++) {
+ // if the values are equal, no changes are needed
+ if( p1[i] == p2[i] ) { continue; }
+
+ // if the current value is less than the target, increase it by one
+ if( p1[i] < p2[i] ) { p1[i]++; changes++; }
+
+ // if the current value is greater than the target,
+ // increase it by one (or two if it's still greater).
+ if( p1[i] > p2[i] ) {
+ p1[i]--; changes++;
+ if( p1[i] > p2[i] ) { p1[i]--; }
+ }
+
+ // if we've hit the maximum number of changes, exit
+ if( changes >= maxChanges) { break; }
+ }
+}
+
+
+uint8_t applyGamma_video( uint8_t brightness, float gamma)
+{
+ float orig;
+ float adj;
+ orig = (float)(brightness) / (255.0);
+ adj = pow( orig, gamma) * (255.0);
+ uint8_t result = (uint8_t)(adj);
+ if( (brightness > 0) && (result == 0)) {
+ result = 1; // never gamma-adjust a positive number down to zero
+ }
+ return result;
+}
+
+CRGB applyGamma_video( const CRGB& orig, float gamma)
+{
+ CRGB adj;
+ adj.r = applyGamma_video( orig.r, gamma);
+ adj.g = applyGamma_video( orig.g, gamma);
+ adj.b = applyGamma_video( orig.b, gamma);
+ return adj;
+}
+
+CRGB applyGamma_video( const CRGB& orig, float gammaR, float gammaG, float gammaB)
+{
+ CRGB adj;
+ adj.r = applyGamma_video( orig.r, gammaR);
+ adj.g = applyGamma_video( orig.g, gammaG);
+ adj.b = applyGamma_video( orig.b, gammaB);
+ return adj;
+}
+
+CRGB& napplyGamma_video( CRGB& rgb, float gamma)
+{
+ rgb = applyGamma_video( rgb, gamma);
+ return rgb;
+}
+
+CRGB& napplyGamma_video( CRGB& rgb, float gammaR, float gammaG, float gammaB)
+{
+ rgb = applyGamma_video( rgb, gammaR, gammaG, gammaB);
+ return rgb;
+}
+
+void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gamma)
+{
+ for( uint16_t i = 0; i < count; i++) {
+ rgbarray[i] = applyGamma_video( rgbarray[i], gamma);
+ }
+}
+
+void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gammaR, float gammaG, float gammaB)
+{
+ for( uint16_t i = 0; i < count; i++) {
+ rgbarray[i] = applyGamma_video( rgbarray[i], gammaR, gammaG, gammaB);
+ }
+}
+
+
+FASTLED_NAMESPACE_END
diff --git a/colorutils.h b/colorutils.h
index 9034c1ee..209e6410 100644
--- a/colorutils.h
+++ b/colorutils.h
@@ -1,27 +1,36 @@
#ifndef __INC_COLORUTILS_H
#define __INC_COLORUTILS_H
-#include <avr/pgmspace.h>
+///@file colorutils.h
+/// functions for color fill, paletters, blending, and more
#include "pixeltypes.h"
+#include "fastled_progmem.h"
+FASTLED_NAMESPACE_BEGIN
+///@defgroup Colorutils Color utility functions
+///A variety of functions for working with color, palletes, and leds
+///@{
-// fill_solid - fill a range of LEDs with a solid color
-// Example: fill_solid( leds, NUM_LEDS, CRGB(50,0,200));
-
+/// fill_solid - fill a range of LEDs with a solid color
+/// Example: fill_solid( leds, NUM_LEDS, CRGB(50,0,200));
void fill_solid( struct CRGB * leds, int numToFill,
const struct CRGB& color);
+/// fill_solid - fill a range of LEDs with a solid color
+/// Example: fill_solid( leds, NUM_LEDS, CRGB(50,0,200));
void fill_solid( struct CHSV* targetArray, int numToFill,
const struct CHSV& hsvColor);
-
-// fill_rainbow - fill a range of LEDs with a rainbow of colors, at
-// full saturation and full value (brightness)
+
+/// fill_rainbow - fill a range of LEDs with a rainbow of colors, at
+/// full saturation and full value (brightness)
void fill_rainbow( struct CRGB * pFirstLED, int numToFill,
uint8_t initialhue,
uint8_t deltahue = 5);
-
+
+/// fill_rainbow - fill a range of LEDs with a rainbow of colors, at
+/// full saturation and full value (brightness)
void fill_rainbow( struct CHSV * targetArray, int numToFill,
uint8_t initialhue,
uint8_t deltahue = 5);
@@ -43,11 +52,11 @@ void fill_rainbow( struct CHSV * targetArray, int numToFill,
//
// fill_gradient can write the gradient colors EITHER
// (1) into an array of CRGBs (e.g., into leds[] array, or an RGB Palette)
-// OR
+// OR
// (2) into an array of CHSVs (e.g. an HSV Palette).
//
-// In the case of writing into a CRGB array, the gradient is
-// computed in HSV space, and then HSV values are converted to RGB
+// In the case of writing into a CRGB array, the gradient is
+// computed in HSV space, and then HSV values are converted to RGB
// as they're written into the RGB array.
typedef enum { FORWARD_HUES, BACKWARD_HUES, SHORTEST_HUES, LONGEST_HUES } TGradientDirectionCode;
@@ -56,6 +65,30 @@ typedef enum { FORWARD_HUES, BACKWARD_HUES, SHORTEST_HUES, LONGEST_HUES } TGradi
#define saccum87 int16_t
+/// fill_gradient - fill an array of colors with a smooth HSV gradient
+/// between two specified HSV colors.
+/// Since 'hue' is a value around a color wheel,
+/// there are always two ways to sweep from one hue
+/// to another.
+/// This function lets you specify which way you want
+/// the hue gradient to sweep around the color wheel:
+///
+/// FORWARD_HUES: hue always goes clockwise
+/// BACKWARD_HUES: hue always goes counter-clockwise
+/// SHORTEST_HUES: hue goes whichever way is shortest
+/// LONGEST_HUES: hue goes whichever way is longest
+///
+/// The default is SHORTEST_HUES, as this is nearly
+/// always what is wanted.
+///
+/// fill_gradient can write the gradient colors EITHER
+/// (1) into an array of CRGBs (e.g., into leds[] array, or an RGB Palette)
+/// OR
+/// (2) into an array of CHSVs (e.g. an HSV Palette).
+///
+/// In the case of writing into a CRGB array, the gradient is
+/// computed in HSV space, and then HSV values are converted to RGB
+/// as they're written into the RGB array.
template <typename T>
void fill_gradient( T* targetArray,
uint16_t startpos, CHSV startcolor,
@@ -66,12 +99,12 @@ void fill_gradient( T* targetArray,
if( endpos < startpos ) {
uint16_t t = endpos;
CHSV tc = endcolor;
- startpos = t;
- startcolor = tc;
endcolor = startcolor;
endpos = startpos;
+ startpos = t;
+ startcolor = tc;
}
-
+
// If we're fading toward black (val=0) or white (sat=0),
// then set the endhue to the starthue.
// This lets us ramp smoothly to black or white, regardless
@@ -79,7 +112,7 @@ void fill_gradient( T* targetArray,
if( endcolor.value == 0 || endcolor.saturation == 0) {
endcolor.hue = startcolor.hue;
}
-
+
// Similarly, if we're fading in from black (val=0) or white (sat=0)
// then set the starthue to the endhue.
// This lets us ramp smoothly up from black or white, regardless
@@ -87,30 +120,30 @@ void fill_gradient( T* targetArray,
if( startcolor.value == 0 || startcolor.saturation == 0) {
startcolor.hue = endcolor.hue;
}
-
+
saccum87 huedistance87;
saccum87 satdistance87;
saccum87 valdistance87;
-
+
satdistance87 = (endcolor.sat - startcolor.sat) << 7;
valdistance87 = (endcolor.val - startcolor.val) << 7;
-
+
uint8_t huedelta8 = endcolor.hue - startcolor.hue;
-
+
if( directionCode == SHORTEST_HUES ) {
directionCode = FORWARD_HUES;
if( huedelta8 > 127) {
directionCode = BACKWARD_HUES;
}
}
-
+
if( directionCode == LONGEST_HUES ) {
directionCode = FORWARD_HUES;
if( huedelta8 < 128) {
directionCode = BACKWARD_HUES;
}
}
-
+
if( directionCode == FORWARD_HUES) {
huedistance87 = huedelta8 << 7;
}
@@ -119,18 +152,18 @@ void fill_gradient( T* targetArray,
huedistance87 = (uint8_t)(256 - huedelta8) << 7;
huedistance87 = -huedistance87;
}
-
+
uint16_t pixeldistance = endpos - startpos;
int16_t divisor = pixeldistance ? pixeldistance : 1;
-
+
saccum87 huedelta87 = huedistance87 / divisor;
saccum87 satdelta87 = satdistance87 / divisor;
saccum87 valdelta87 = valdistance87 / divisor;
-
+
huedelta87 *= 2;
satdelta87 *= 2;
valdelta87 *= 2;
-
+
accum88 hue88 = startcolor.hue << 8;
accum88 sat88 = startcolor.sat << 8;
accum88 val88 = startcolor.val << 8;
@@ -146,7 +179,7 @@ void fill_gradient( T* targetArray,
// Convenience functions to fill an array of colors with a
// two-color, three-color, or four-color gradient
template <typename T>
-void fill_gradient( T* targetArray, uint16_t numLeds, const CHSV& c1, const CHSV& c2,
+void fill_gradient( T* targetArray, uint16_t numLeds, const CHSV& c1, const CHSV& c2,
TGradientDirectionCode directionCode = SHORTEST_HUES )
{
uint16_t last = numLeds - 1;
@@ -154,8 +187,8 @@ void fill_gradient( T* targetArray, uint16_t numLeds, const CHSV& c1, const CHSV
}
template <typename T>
-void fill_gradient( T* targetArray, uint16_t numLeds,
- const CHSV& c1, const CHSV& c2, const CHSV& c3,
+void fill_gradient( T* targetArray, uint16_t numLeds,
+ const CHSV& c1, const CHSV& c2, const CHSV& c3,
TGradientDirectionCode directionCode = SHORTEST_HUES )
{
uint16_t half = (numLeds / 2);
@@ -165,8 +198,8 @@ void fill_gradient( T* targetArray, uint16_t numLeds,
}
template <typename T>
-void fill_gradient( T* targetArray, uint16_t numLeds,
- const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4,
+void fill_gradient( T* targetArray, uint16_t numLeds,
+ const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4,
TGradientDirectionCode directionCode = SHORTEST_HUES )
{
uint16_t onethird = (numLeds / 3);
@@ -219,6 +252,19 @@ void fade_raw( CRGB* leds, uint16_t num_leds, uint8_t fadeBy);
// way down to black even if 'scale' is not zero.
void nscale8( CRGB* leds, uint16_t num_leds, uint8_t scale);
+// fadeUsingColor - scale down the brightness of an array of pixels,
+// as though it were seen through a transparent
+// filter with the specified color.
+// For example, if the colormask is
+// CRGB( 200, 100, 50)
+// then the pixels' red will be faded to 200/256ths,
+// their green to 100/256ths, and their blue to 50/256ths.
+// This particular example give a 'hot fade' look,
+// with white fading to yellow, then red, then black.
+// You can also use colormasks like CRGB::Blue to
+// zero out the red and green elements, leaving blue
+// (largely) the same.
+void fadeUsingColor( CRGB* leds, uint16_t numLeds, const CRGB& colormask);
// Pixel blending
@@ -256,6 +302,27 @@ void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOver
TGradientDirectionCode directionCode = SHORTEST_HUES);
+// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors.
+// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors.
+//
+// 0 = no spread at all
+// 64 = moderate spreading
+// 172 = maximum smooth, even spreading
+//
+// 173..255 = wider spreading, but increasing flicker
+//
+// Total light is NOT entirely conserved, so many repeated
+// calls to 'blur' will also result in the light fading,
+// eventually all the way to black; this is by design so that
+// it can be used to (slowly) clear the LEDs to black.
+void blur1d( CRGB* leds, uint16_t numLeds, fract8 blur_amount);
+void blur2d( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount);
+
+// blurRows: perform a blur1d on every row of a rectangular matrix
+void blurRows( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount);
+// blurColumns: perform a blur1d on each column of a rectangular matrix
+void blurColumns(CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount);
+
// CRGB HeatColor( uint8_t temperature)
//
@@ -329,12 +396,31 @@ class CHSVPalette16;
class CHSVPalette256;
typedef uint32_t TProgmemRGBPalette16[16];
typedef uint32_t TProgmemHSVPalette16[16];
-#define TProgmemPalette16 TProgmemRGBPalette16
+#define TProgmemPalette16 TProgmemRGBPalette16
+
+typedef const uint8_t TProgmemRGBGradientPalette_byte ;
+typedef const TProgmemRGBGradientPalette_byte *TProgmemRGBGradientPalette_bytes;
+typedef TProgmemRGBGradientPalette_bytes TProgmemRGBGradientPalettePtr;
+typedef union {
+ struct {
+ uint8_t index;
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ };
+ uint32_t dword;
+ uint8_t bytes[4];
+} TRGBGradientPaletteEntryUnion;
+
+typedef uint8_t TDynamicRGBGradientPalette_byte ;
+typedef const TDynamicRGBGradientPalette_byte *TDynamicRGBGradientPalette_bytes;
+typedef TDynamicRGBGradientPalette_bytes TDynamicRGBGradientPalettePtr;
// Convert a 16-entry palette to a 256-entry palette
void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette256& destpal256);
void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette256& destpal256);
+
class CHSVPalette16 {
public:
CHSV entries[16];
@@ -349,7 +435,7 @@ public:
entries[8]=c08; entries[9]=c09; entries[10]=c10; entries[11]=c11;
entries[12]=c12; entries[13]=c13; entries[14]=c14; entries[15]=c15;
};
-
+
CHSVPalette16( const CHSVPalette16& rhs)
{
memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
@@ -359,11 +445,11 @@ public:
memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
return *this;
}
-
+
CHSVPalette16( const TProgmemHSVPalette16& rhs)
{
for( uint8_t i = 0; i < 16; i++) {
- CRGB xyz = pgm_read_dword_near( rhs + i);
+ CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i);
entries[i].hue = xyz.red;
entries[i].sat = xyz.green;
entries[i].val = xyz.blue;
@@ -372,14 +458,14 @@ public:
CHSVPalette16& operator=( const TProgmemHSVPalette16& rhs)
{
for( uint8_t i = 0; i < 16; i++) {
- CRGB xyz = pgm_read_dword_near( rhs + i);
+ CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i);
entries[i].hue = xyz.red;
entries[i].sat = xyz.green;
entries[i].val = xyz.blue;
}
return *this;
}
-
+
inline CHSV& operator[] (uint8_t x) __attribute__((always_inline))
{
return entries[x];
@@ -388,7 +474,7 @@ public:
{
return entries[x];
}
-
+
inline CHSV& operator[] (int x) __attribute__((always_inline))
{
return entries[(uint8_t)x];
@@ -397,12 +483,12 @@ public:
{
return entries[(uint8_t)x];
}
-
+
operator CHSV*()
{
return &(entries[0]);
}
-
+
CHSVPalette16( const CHSV& c1)
{
fill_solid( &(entries[0]), 16, c1);
@@ -419,7 +505,7 @@ public:
{
fill_gradient( &(entries[0]), 16, c1, c2, c3, c4);
}
-
+
};
class CHSVPalette256 {
@@ -435,7 +521,7 @@ public:
c08,c09,c10,c11,c12,c13,c14,c15);
*this = p16;
};
-
+
CHSVPalette256( const CHSVPalette256& rhs)
{
memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
@@ -445,7 +531,7 @@ public:
memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
return *this;
}
-
+
CHSVPalette256( const CHSVPalette16& rhs16)
{
UpscalePalette( rhs16, *this);
@@ -455,7 +541,7 @@ public:
UpscalePalette( rhs16, *this);
return *this;
}
-
+
CHSVPalette256( const TProgmemRGBPalette16& rhs)
{
CHSVPalette16 p16(rhs);
@@ -467,7 +553,7 @@ public:
*this = p16;
return *this;
}
-
+
inline CHSV& operator[] (uint8_t x) __attribute__((always_inline))
{
return entries[x];
@@ -476,7 +562,7 @@ public:
{
return entries[x];
}
-
+
inline CHSV& operator[] (int x) __attribute__((always_inline))
{
return entries[(uint8_t)x];
@@ -490,7 +576,7 @@ public:
{
return &(entries[0]);
}
-
+
CHSVPalette256( const CHSV& c1)
{
fill_solid( &(entries[0]), 256, c1);
@@ -523,7 +609,7 @@ public:
entries[8]=c08; entries[9]=c09; entries[10]=c10; entries[11]=c11;
entries[12]=c12; entries[13]=c13; entries[14]=c14; entries[15]=c15;
};
-
+
CRGBPalette16( const CRGBPalette16& rhs)
{
memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
@@ -533,13 +619,13 @@ public:
memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
return *this;
}
-
+
CRGBPalette16( const CHSVPalette16& rhs)
{
for( uint8_t i = 0; i < 16; i++) {
entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion
}
- }
+ }
CRGBPalette16& operator=( const CHSVPalette16& rhs)
{
for( uint8_t i = 0; i < 16; i++) {
@@ -551,17 +637,17 @@ public:
CRGBPalette16( const TProgmemRGBPalette16& rhs)
{
for( uint8_t i = 0; i < 16; i++) {
- entries[i] = pgm_read_dword_near( rhs + i);
+ entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i);
}
}
CRGBPalette16& operator=( const TProgmemRGBPalette16& rhs)
{
for( uint8_t i = 0; i < 16; i++) {
- entries[i] = pgm_read_dword_near( rhs + i);
+ entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i);
}
return *this;
}
-
+
inline CRGB& operator[] (uint8_t x) __attribute__((always_inline))
{
return entries[x];
@@ -570,7 +656,7 @@ public:
{
return entries[x];
}
-
+
inline CRGB& operator[] (int x) __attribute__((always_inline))
{
return entries[(uint8_t)x];
@@ -579,12 +665,12 @@ public:
{
return entries[(uint8_t)x];
}
-
+
operator CRGB*()
{
return &(entries[0]);
}
-
+
CRGBPalette16( const CHSV& c1)
{
fill_solid( &(entries[0]), 16, c1);
@@ -601,7 +687,7 @@ public:
{
fill_gradient( &(entries[0]), 16, c1, c2, c3, c4);
}
-
+
CRGBPalette16( const CRGB& c1)
{
fill_solid( &(entries[0]), 16, c1);
@@ -618,7 +704,119 @@ public:
{
fill_gradient_RGB( &(entries[0]), 16, c1, c2, c3, c4);
}
-
+
+
+ // Gradient palettes are loaded into CRGB16Palettes in such a way
+ // that, if possible, every color represented in the gradient palette
+ // is also represented in the CRGBPalette16.
+ // For example, consider a gradient palette that is all black except
+ // for a single, one-element-wide (1/256th!) spike of red in the middle:
+ // 0, 0,0,0
+ // 124, 0,0,0
+ // 125, 255,0,0 // one 1/256th-palette-wide red stripe
+ // 126, 0,0,0
+ // 255, 0,0,0
+ // A naive conversion of this 256-element palette to a 16-element palette
+ // might accidentally completely eliminate the red spike, rendering the
+ // palette completely black.
+ // However, the conversions provided here would attempt to include a
+ // the red stripe in the output, more-or-less as faithfully as possible.
+ // So in this case, the resulting CRGBPalette16 palette would have a red
+ // stripe in the middle which was 1/16th of a palette wide -- the
+ // narrowest possible in a CRGBPalette16.
+ // This means that the relative width of stripes in a CRGBPalette16
+ // will be, by definition, different from the widths in the gradient
+ // palette. This code attempts to preserve "all the colors", rather than
+ // the exact stripe widths at the expense of dropping some colors.
+ CRGBPalette16( TProgmemRGBGradientPalette_bytes progpal )
+ {
+ *this = progpal;
+ }
+ CRGBPalette16& operator=( TProgmemRGBGradientPalette_bytes progpal )
+ {
+ TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal);
+ TRGBGradientPaletteEntryUnion u;
+
+ // Count entries
+ uint8_t count = 0;
+ do {
+ u.dword = FL_PGM_READ_DWORD_NEAR(progent + count);
+ count++;;
+ } while ( u.index != 255);
+
+ int8_t lastSlotUsed = -1;
+
+ u.dword = FL_PGM_READ_DWORD_NEAR( progent);
+ CRGB rgbstart( u.r, u.g, u.b);
+
+ int indexstart = 0;
+ uint8_t istart8 = 0;
+ uint8_t iend8 = 0;
+ while( indexstart < 255) {
+ progent++;
+ u.dword = FL_PGM_READ_DWORD_NEAR( progent);
+ int indexend = u.index;
+ CRGB rgbend( u.r, u.g, u.b);
+ istart8 = indexstart / 16;
+ iend8 = indexend / 16;
+ if( count < 16) {
+ if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) {
+ istart8 = lastSlotUsed + 1;
+ if( iend8 < istart8) {
+ iend8 = istart8;
+ }
+ }
+ lastSlotUsed = iend8;
+ }
+ fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend);
+ indexstart = indexend;
+ rgbstart = rgbend;
+ }
+ return *this;
+ }
+ CRGBPalette16& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal )
+ {
+ TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal);
+ TRGBGradientPaletteEntryUnion u;
+
+ // Count entries
+ uint8_t count = 0;
+ do {
+ u = *(ent + count);
+ count++;;
+ } while ( u.index != 255);
+
+ int8_t lastSlotUsed = -1;
+
+
+ u = *ent;
+ CRGB rgbstart( u.r, u.g, u.b);
+
+ int indexstart = 0;
+ uint8_t istart8 = 0;
+ uint8_t iend8 = 0;
+ while( indexstart < 255) {
+ ent++;
+ u = *ent;
+ int indexend = u.index;
+ CRGB rgbend( u.r, u.g, u.b);
+ istart8 = indexstart / 16;
+ iend8 = indexend / 16;
+ if( count < 16) {
+ if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) {
+ istart8 = lastSlotUsed + 1;
+ if( iend8 < istart8) {
+ iend8 = istart8;
+ }
+ }
+ lastSlotUsed = iend8;
+ }
+ fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend);
+ indexstart = indexend;
+ rgbstart = rgbend;
+ }
+ return *this;
+ }
};
@@ -635,7 +833,7 @@ public:
c08,c09,c10,c11,c12,c13,c14,c15);
*this = p16;
};
-
+
CRGBPalette256( const CRGBPalette256& rhs)
{
memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
@@ -645,7 +843,7 @@ public:
memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
return *this;
}
-
+
CRGBPalette256( const CHSVPalette256& rhs)
{
for( int i = 0; i < 256; i++) {
@@ -669,7 +867,7 @@ public:
UpscalePalette( rhs16, *this);
return *this;
}
-
+
CRGBPalette256( const TProgmemRGBPalette16& rhs)
{
CRGBPalette16 p16(rhs);
@@ -681,7 +879,7 @@ public:
*this = p16;
return *this;
}
-
+
inline CRGB& operator[] (uint8_t x) __attribute__((always_inline))
{
return entries[x];
@@ -690,7 +888,7 @@ public:
{
return entries[x];
}
-
+
inline CRGB& operator[] (int x) __attribute__((always_inline))
{
return entries[(uint8_t)x];
@@ -704,7 +902,7 @@ public:
{
return &(entries[0]);
}
-
+
CRGBPalette256( const CHSV& c1)
{
fill_solid( &(entries[0]), 256, c1);
@@ -721,7 +919,7 @@ public:
{
fill_gradient( &(entries[0]), 256, c1, c2, c3, c4);
}
-
+
CRGBPalette256( const CRGB& c1)
{
fill_solid( &(entries[0]), 256, c1);
@@ -738,18 +936,65 @@ public:
{
fill_gradient_RGB( &(entries[0]), 256, c1, c2, c3, c4);
}
-
+
+ CRGBPalette256( TProgmemRGBGradientPalette_bytes progpal )
+ {
+ *this = progpal;
+ }
+ CRGBPalette256& operator=( TProgmemRGBGradientPalette_bytes progpal )
+ {
+ TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal);
+ TRGBGradientPaletteEntryUnion u;
+ u.dword = FL_PGM_READ_DWORD_NEAR( progent);
+ CRGB rgbstart( u.r, u.g, u.b);
+
+ int indexstart = 0;
+ while( indexstart < 255) {
+ progent++;
+ u.dword = FL_PGM_READ_DWORD_NEAR( progent);
+ int indexend = u.index;
+ CRGB rgbend( u.r, u.g, u.b);
+ fill_gradient_RGB( &(entries[0]), indexstart, rgbstart, indexend, rgbend);
+ indexstart = indexend;
+ rgbstart = rgbend;
+ }
+ return *this;
+ }
+ CRGBPalette256& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal )
+ {
+ TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal);
+ TRGBGradientPaletteEntryUnion u;
+ u = *ent;
+ CRGB rgbstart( u.r, u.g, u.b);
+
+ int indexstart = 0;
+ while( indexstart < 255) {
+ ent++;
+ u = *ent;
+ int indexend = u.index;
+ CRGB rgbend( u.r, u.g, u.b);
+ fill_gradient_RGB( &(entries[0]), indexstart, rgbstart, indexend, rgbend);
+ indexstart = indexend;
+ rgbstart = rgbend;
+ }
+ return *this;
+ }
};
-typedef enum { NOBLEND=0, BLEND=1 } TBlendType;
+typedef enum { NOBLEND=0, LINEARBLEND=1 } TBlendType;
CRGB ColorFromPalette( const CRGBPalette16& pal,
+ uint8_t index,
+ uint8_t brightness=255,
+ TBlendType blendType=LINEARBLEND);
+
+CRGB ColorFromPalette( const TProgmemRGBPalette16& pal,
uint8_t index,
uint8_t brightness=255,
- TBlendType blendType=BLEND);
+ TBlendType blendType=LINEARBLEND);
CRGB ColorFromPalette( const CRGBPalette256& pal,
uint8_t index,
@@ -759,7 +1004,7 @@ CRGB ColorFromPalette( const CRGBPalette256& pal,
CHSV ColorFromPalette( const CHSVPalette16& pal,
uint8_t index,
uint8_t brightness=255,
- TBlendType blendType=BLEND);
+ TBlendType blendType=LINEARBLEND);
CHSV ColorFromPalette( const CHSVPalette256& pal,
uint8_t index,
@@ -768,7 +1013,7 @@ CHSV ColorFromPalette( const CHSVPalette256& pal,
// Fill a range of LEDs with a sequece of entryies from a palette
-template <typename PALETTE>
+template <typename PALETTE>
void fill_palette(CRGB* L, uint16_t N, uint8_t startIndex, uint8_t incIndex,
const PALETTE& pal, uint8_t brightness, TBlendType blendType)
{
@@ -780,13 +1025,13 @@ void fill_palette(CRGB* L, uint16_t N, uint8_t startIndex, uint8_t incIndex,
}
template <typename PALETTE>
-void map_data_into_colors_through_palette(
- uint8_t *dataArray, uint16_t dataCount,
- CRGB* targetColorArray,
- const PALETTE& pal,
- uint8_t brightness=255,
- uint8_t opacity=255,
- TBlendType blendType=BLEND)
+void map_data_into_colors_through_palette(
+ uint8_t *dataArray, uint16_t dataCount,
+ CRGB* targetColorArray,
+ const PALETTE& pal,
+ uint8_t brightness=255,
+ uint8_t opacity=255,
+ TBlendType blendType=LINEARBLEND)
{
for( uint16_t i = 0; i < dataCount; i++) {
uint8_t d = dataArray[i];
@@ -801,4 +1046,120 @@ void map_data_into_colors_through_palette(
}
}
+// nblendPaletteTowardPalette:
+// Alter one palette by making it slightly more like
+// a 'target palette', used for palette cross-fades.
+//
+// It does this by comparing each of the R, G, and B channels
+// of each entry in the current palette to the corresponding
+// entry in the target palette and making small adjustments:
+// If the Red channel is too low, it will be increased.
+// If the Red channel is too high, it will be slightly reduced.
+// ... and likewise for Green and Blue channels.
+//
+// Additionally, there are two significant visual improvements
+// to this algorithm implemented here. First is this:
+// When increasing a channel, it is stepped up by ONE.
+// When decreasing a channel, it is stepped down by TWO.
+// Due to the way the eye perceives light, and the way colors
+// are represented in RGB, this produces a more uniform apparent
+// brightness when cross-fading between most palette colors.
+//
+// The second visual tweak is limiting the number of changes
+// that will be made to the palette at once. If all the palette
+// entries are changed at once, it can give a muddled appearance.
+// However, if only a few palette entries are changed at once,
+// you get a visually smoother transition: in the middle of the
+// cross-fade your current palette will actually contain some
+// colors from the old palette, a few blended colors, and some
+// colors from the new palette.
+// The maximum number of possible palette changes per call
+// is 48 (sixteen color entries time three channels each).
+// The default 'maximim number of changes' here is 12, meaning
+// that only approximately a quarter of the palette entries
+// will be changed per call.
+void nblendPaletteTowardPalette( CRGBPalette16& currentPalette,
+ CRGBPalette16& targetPalette,
+ uint8_t maxChanges=24);
+
+
+
+
+// You can also define a static RGB palette very compactly in terms of a series
+// of connected color gradients.
+// For example, if you want the first 3/4ths of the palette to be a slow
+// gradient ramping from black to red, and then the remaining 1/4 of the
+// palette to be a quicker ramp to white, you specify just three points: the
+// starting black point (at index 0), the red midpoint (at index 192),
+// and the final white point (at index 255). It looks like this:
+//
+// index: 0 192 255
+// |----------r-r-r-rrrrrrrrRrRrRrRrRRRR-|-RRWRWWRWWW-|
+// color: (0,0,0) (255,0,0) (255,255,255)
+//
+// Here's how you'd define that gradient palette:
+//
+// DEFINE_GRADIENT_PALETTE( black_to_red_to_white_p ) {
+// 0, 0, 0, 0, /* at index 0, black(0,0,0) */
+// 192, 255, 0, 0, /* at index 192, red(255,0,0) */
+// 255, 255,255,255 /* at index 255, white(255,255,255) */
+// };
+//
+// This format is designed for compact storage. The example palette here
+// takes up just 12 bytes of PROGMEM (flash) storage, and zero bytes
+// of SRAM when not currently in use.
+//
+// To use one of these gradient palettes, simply assign it into a
+// CRGBPalette16 or a CRGBPalette256, like this:
+//
+// CRGBPalette16 pal = black_to_red_to_white_p;
+//
+// When the assignment is made, the gradients are expanded out into
+// either 16 or 256 palette entries, depending on the kind of palette
+// object they're assigned to.
+//
+// IMPORTANT NOTES & CAVEATS:
+//
+// - The last 'index' position MUST BE 255! Failure to end with
+// index 255 will result in program hangs or crashes.
+//
+// - At this point, these gradient palette definitions MUST BE
+// stored in PROGMEM on AVR-based Arduinos. If you use the
+// DEFINE_GRADIENT_PALETTE macro, this is taken care of automatically.
+//
+
+#define DEFINE_GRADIENT_PALETTE(X) \
+ extern const TProgmemRGBGradientPalette_byte X[] FL_PROGMEM =
+
+#define DECLARE_GRADIENT_PALETTE(X) \
+ extern const TProgmemRGBGradientPalette_byte X[] FL_PROGMEM
+
+
+// Functions to apply gamma adjustments, either:
+// - a single gamma adjustment to a single scalar value,
+// - a single gamma adjustment to each channel of a CRGB color, or
+// - different gamma adjustments for each channel of a CRFB color.
+//
+// Note that the gamma is specified as a traditional floating point value
+// e.g., "2.5", and as such these functions should not be called in
+// your innermost pixel loops, or in animations that are extremely
+// low on program storage space. Nevertheless, if you need these
+// functions, here they are.
+//
+// Furthermore, bear in mind that CRGB leds have only eight bits
+// per channel of color resolution, and that very small, subtle shadings
+// may not be visible.
+uint8_t applyGamma_video( uint8_t brightness, float gamma);
+CRGB applyGamma_video( const CRGB& orig, float gamma);
+CRGB applyGamma_video( const CRGB& orig, float gammaR, float gammaG, float gammaB);
+// The "n" versions below modify their arguments in-place.
+CRGB& napplyGamma_video( CRGB& rgb, float gamma);
+CRGB& napplyGamma_video( CRGB& rgb, float gammaR, float gammaG, float gammaB);
+void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gamma);
+void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gammaR, float gammaG, float gammaB);
+
+
+FASTLED_NAMESPACE_END
+
+///@}
#endif
diff --git a/controller.h b/controller.h
index a2519e1f..b9929a4f 100644
--- a/controller.h
+++ b/controller.h
@@ -1,10 +1,15 @@
#ifndef __INC_CONTROLLER_H
#define __INC_CONTROLLER_H
+///@file controller.h
+/// base definitions used by led controllers for writing out led data
+
#include "led_sysdefs.h"
#include "pixeltypes.h"
#include "color.h"
+FASTLED_NAMESPACE_BEGIN
+
#define RO(X) RGB_BYTE(RGB_ORDER, X)
#define RGB_BYTE(RO,X) (((RO)>>(3*(2-(X)))) & 0x3)
@@ -41,11 +46,16 @@ protected:
static CLEDController *m_pHead;
static CLEDController *m_pTail;
- // set all the leds on the controller to a given color
+ /// set all the leds on the controller to a given color
+ ///@param data the crgb color to set the leds to
+ ///@param nLeds the numner of leds to set to this color
+ ///@param scale the rgb scaling value for outputting color
virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) = 0;
- // note that the uint8_ts will be in the order that you want them sent out to the device.
- // nLeds is the number of RGB leds being written to
+ /// write the passed in rgb data out to the leds managed by this controller
+ ///@param data the rgb data to write out to the strip
+ ///@param nLeds the number of leds being written out
+ ///@param scale the rgb scaling to apply to each led before writing it out
virtual void show(const struct CRGB *data, int nLeds, CRGB scale) = 0;
#ifdef SUPPORT_ARGB
@@ -53,6 +63,7 @@ protected:
virtual void show(const struct CARGB *data, int nLeds, CRGB scale) = 0;
#endif
public:
+ /// create an led controller object, add it to the chain of controllers
CLEDController() : m_Data(NULL), m_ColorCorrection(UncorrectedColor), m_ColorTemperature(UncorrectedTemperature), m_DitherMode(BINARY_DITHER), m_nLeds(0) {
m_pNext = NULL;
if(m_pHead==NULL) { m_pHead = this; }
@@ -60,33 +71,35 @@ public:
m_pTail = this;
}
- // initialize the LED controller
+ ///initialize the LED controller
virtual void init() = 0;
- // clear out/zero out the given number of leds.
+ ///clear out/zero out the given number of leds.
virtual void clearLeds(int nLeds) = 0;
- // show function w/integer brightness, will scale for color correction and temperature
+ /// show function w/integer brightness, will scale for color correction and temperature
void show(const struct CRGB *data, int nLeds, uint8_t brightness) {
show(data, nLeds, getAdjustment(brightness));
}
- // show function w/integer brightness, will scale for color correction and temperature
+ /// show function w/integer brightness, will scale for color correction and temperature
void showColor(const struct CRGB &data, int nLeds, uint8_t brightness) {
showColor(data, nLeds, getAdjustment(brightness));
}
- // show function using the "attached to this controller" led data
+ /// show function using the "attached to this controller" led data
void showLeds(uint8_t brightness=255) {
show(m_Data, m_nLeds, getAdjustment(brightness));
}
+ /// show the given color on the led strip
void showColor(const struct CRGB & data, uint8_t brightness=255) {
showColor(data, m_nLeds, getAdjustment(brightness));
}
- // navigating the list of controllers
+ /// get the first led controller in the chain of controllers
static CLEDController *head() { return m_pHead; }
+ /// get the next controller in the chain after this one. will return NULL at the end of the chain
CLEDController *next() { return m_pNext; }
#ifdef SUPPORT_ARGB
@@ -96,64 +109,80 @@ public:
}
#endif
+ /// set the default array of leds to be used by this controller
CLEDController & setLeds(CRGB *data, int nLeds) {
m_Data = data;
m_nLeds = nLeds;
return *this;
}
+ /// zero out the led data managed by this controller
void clearLedData() {
if(m_Data) {
memset8((void*)m_Data, 0, sizeof(struct CRGB) * m_nLeds);
}
}
- // How many leds does this controller manage?
+ /// How many leds does this controller manage?
int size() { return m_nLeds; }
- // Pointer to the CRGB array for this controller
+ /// Pointer to the CRGB array for this controller
CRGB* leds() { return m_Data; }
- // Reference to the n'th item in the controller
+ /// Reference to the n'th item in the controller
CRGB &operator[](int x) { return m_Data[x]; }
+ /// set the dithering mode for this controller to use
inline CLEDController & setDither(uint8_t ditherMode = BINARY_DITHER) { m_DitherMode = ditherMode; return *this; }
+ /// get the dithering option currently set for this controller
inline uint8_t getDither() { return m_DitherMode; }
+ /// the the color corrction to use for this controller, expressed as an rgb object
CLEDController & setCorrection(CRGB correction) { m_ColorCorrection = correction; return *this; }
+ /// set the color correction to use for this controller
CLEDController & setCorrection(LEDColorCorrection correction) { m_ColorCorrection = correction; return *this; }
+ /// get the correction value used by this controller
CRGB getCorrection() { return m_ColorCorrection; }
+ /// set the color temperature, aka white point, for this controller
CLEDController & setTemperature(CRGB temperature) { m_ColorTemperature = temperature; return *this; }
+ /// set the color temperature, aka white point, for this controller
CLEDController & setTemperature(ColorTemperature temperature) { m_ColorTemperature = temperature; return *this; }
+ /// get the color temperature, aka whipe point, for this controller
CRGB getTemperature() { return m_ColorTemperature; }
+ /// Get the combined brightness/color adjustment for this controller
CRGB getAdjustment(uint8_t scale) {
-#if defined(NO_CORRECTION) && (NO_CORRECTION==1)
- return CRGB(scale,scale,scale);
-#else
- CRGB adj(0,0,0);
-
- if(scale > 0) {
- for(uint8_t i = 0; i < 3; i++) {
- uint8_t cc = m_ColorCorrection.raw[i];
- uint8_t ct = m_ColorTemperature.raw[i];
- if(cc > 0 && ct > 0) {
- uint32_t work = (((uint32_t)cc)+1) * (((uint32_t)ct)+1) * scale;
- work /= 0x10000L;
- adj.raw[i] = work & 0xFF;
- }
- }
- }
+ return computeAdjustment(scale, m_ColorCorrection, m_ColorTemperature);
+ }
- return adj;
-#endif
+ static CRGB computeAdjustment(uint8_t scale, const CRGB & colorCorrection, const CRGB & colorTemperature) {
+ #if defined(NO_CORRECTION) && (NO_CORRECTION==1)
+ return CRGB(scale,scale,scale);
+ #else
+ CRGB adj(0,0,0);
+
+ if(scale > 0) {
+ for(uint8_t i = 0; i < 3; i++) {
+ uint8_t cc = colorCorrection.raw[i];
+ uint8_t ct = colorTemperature.raw[i];
+ if(cc > 0 && ct > 0) {
+ uint32_t work = (((uint32_t)cc)+1) * (((uint32_t)ct)+1) * scale;
+ work /= 0x10000L;
+ adj.raw[i] = work & 0xFF;
+ }
+ }
+ }
+
+ return adj;
+ #endif
}
+ virtual uint16_t getMaxRefreshRate() const { return 0; }
};
-// Pixel controller class. This is the class that we use to centralize pixel access in a block of data, including
-// support for things like RGB reordering, scaling, dithering, skipping (for ARGB data), and eventually, we will
-// centralize 8/12/16 conversions here as well.
+/// Pixel controller class. This is the class that we use to centralize pixel access in a block of data, including
+/// support for things like RGB reordering, scaling, dithering, skipping (for ARGB data), and eventually, we will
+/// centralize 8/12/16 conversions here as well.
template<EOrder RGB_ORDER>
struct PixelController {
const uint8_t *mData;
@@ -163,6 +192,7 @@ struct PixelController {
CRGB mScale;
uint8_t mAdvance;
+ ///copy constructor for the pixel controller object
PixelController(const PixelController & other) {
d[0] = other.d[0];
d[1] = other.d[1];
@@ -176,6 +206,15 @@ struct PixelController {
mLen = other.mLen;
}
+
+ /// create a pixel controller for managing led data as it is being written out
+ ///@{
+ ///@param d the led data this controller is managing
+ ///@param len the number of leds this controller is managing
+ ///@param s the combined rgb scaling adjustment for the leds
+ ///@param dither the dither mode for these pixels
+ ///@param advance whether or not to walk through the array of data for each pixel, or just write out the first pixel len times
+ ///@param skip whether or not there is extra data to skip when writing out led data, e.g. if passed in argb data
PixelController(const uint8_t *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER, bool advance=true, uint8_t skip=0) : mData(d), mLen(len), mScale(s) {
enable_dithering(dither);
mData += skip;
@@ -207,7 +246,9 @@ struct PixelController {
mAdvance = 4;
}
#endif
+ ///@}
+ /// initialize the binary dithering for this controller
void init_binary_dithering() {
#if !defined(NO_DITHERING) || (NO_DITHERING != 1)
@@ -235,6 +276,7 @@ struct PixelController {
(UPDATES_PER_FULL_DITHER_CYCLE>64) + \
(UPDATES_PER_FULL_DITHER_CYCLE>128) )
#define VIRTUAL_BITS RECOMMENDED_VIRTUAL_BITS
+
// R is the digther signal 'counter'.
static byte R = 0;
R++;
@@ -283,12 +325,12 @@ struct PixelController {
#endif
}
- // Do we have n pixels left to process?
+ /// Do we have n pixels left to process?
__attribute__((always_inline)) inline bool has(int n) {
return mLen >= n;
}
- // toggle dithering enable
+ /// toggle dithering enable
void enable_dithering(EDitherMode dither) {
switch(dither) {
case BINARY_DITHER: init_binary_dithering(); break;
@@ -296,13 +338,13 @@ struct PixelController {
}
}
- // get the amount to advance the pointer by
+ /// get the amount to advance the pointer by
__attribute__((always_inline)) inline int advanceBy() { return mAdvance; }
- // advance the data pointer forward, adjust position counter
+ /// advance the data pointer forward, adjust position counter
__attribute__((always_inline)) inline void advanceData() { mData += mAdvance; mLen--;}
- // step the dithering forward
+ /// step the dithering forward
__attribute__((always_inline)) inline void stepDithering() {
// IF UPDATING HERE, BE SURE TO UPDATE THE ASM VERSION IN
// clockless_trinket.h!
@@ -311,7 +353,7 @@ struct PixelController {
d[2] = e[2] - d[2];
}
- // Some chipsets pre-cycle the first byte, which means we want to cycle byte 0's dithering separately
+ /// Some chipsets pre-cycle the first byte, which means we want to cycle byte 0's dithering separately
__attribute__((always_inline)) inline void preStepFirstByteDithering() {
d[RO(0)] = e[RO(0)] - d[RO(0)];
}
@@ -329,7 +371,217 @@ struct PixelController {
__attribute__((always_inline)) inline uint8_t loadAndScale1() { return loadAndScale<1>(*this); }
__attribute__((always_inline)) inline uint8_t loadAndScale2() { return loadAndScale<2>(*this); }
__attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0() { return advanceAndLoadAndScale<0>(*this); }
- __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0() { stepDithering(); return advanceAndLoadAndScale<0>(*this); }
+ __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0() { stepDithering(); return advanceAndLoadAndScale<0>(*this); }
+};
+
+// Pixel controller class. This is the class that we use to centralize pixel access in a block of data, including
+// support for things like RGB reordering, scaling, dithering, skipping (for ARGB data), and eventually, we will
+// centralize 8/12/16 conversions here as well.
+template<int LANES,int MASK, EOrder RGB_ORDER>
+struct MultiPixelController {
+ const uint8_t *mData;
+ int mLen;
+ uint8_t d[3];
+ uint8_t e[3];
+ CRGB mScale;
+ int8_t mAdvance;
+ int mOffsets[LANES];
+
+ MultiPixelController(const MultiPixelController & other) {
+ d[0] = other.d[0];
+ d[1] = other.d[1];
+ d[2] = other.d[2];
+ e[0] = other.e[0];
+ e[1] = other.e[1];
+ e[2] = other.e[2];
+ mData = other.mData;
+ mScale = other.mScale;
+ mAdvance = other.mAdvance;
+ mLen = other.mLen;
+ for(int i = 0; i < LANES; i++) { mOffsets[i] = other.mOffsets[i]; }
+
+ }
+
+ void initOffsets(int len) {
+ int nOffset = 0;
+ for(int i = 0; i < LANES; i++) {
+ mOffsets[i] = nOffset;
+ if((1<<i) & MASK) { nOffset += (len * mAdvance); }
+ }
+ }
+
+ MultiPixelController(const uint8_t *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER, bool advance=true, uint8_t skip=0) : mData(d), mLen(len), mScale(s) {
+ enable_dithering(dither);
+ mData += skip;
+ mAdvance = (advance) ? 3+skip : 0;
+ initOffsets(len);
+ }
+
+ MultiPixelController(const CRGB *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)d), mLen(len), mScale(s) {
+ enable_dithering(dither);
+ mAdvance = 3;
+ initOffsets(len);
+ }
+
+ MultiPixelController(const CRGB &d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)&d), mLen(len), mScale(s) {
+ enable_dithering(dither);
+ mAdvance = 0;
+ initOffsets(len);
+ }
+
+#ifdef SUPPORT_ARGB
+ MultiPixelController(const CARGB &d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)&d), mLen(len), mScale(s) {
+ enable_dithering(dither);
+ // skip the A in CARGB
+ mData += 1;
+ mAdvance = 0;
+ initOffsets(len);
+ }
+
+ MultiPixelController(const CARGB *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)d), mLen(len), mScale(s) {
+ enable_dithering(dither);
+ // skip the A in CARGB
+ mData += 1;
+ mAdvance = 4;
+ initOffsets(len);
+ }
+#endif
+
+ void init_binary_dithering() {
+#if !defined(NO_DITHERING) || (NO_DITHERING != 1)
+
+ // Set 'virtual bits' of dithering to the highest level
+ // that is not likely to cause excessive flickering at
+ // low brightness levels + low update rates.
+ // These pre-set values are a little ambitious, since
+ // a 400Hz update rate for WS2811-family LEDs is only
+ // possible with 85 pixels or fewer.
+ // Once we have a 'number of milliseconds since last update'
+ // value available here, we can quickly calculate the correct
+ // number of 'virtual bits' on the fly with a couple of 'if'
+ // statements -- no division required. At this point,
+ // the division is done at compile time, so there's no runtime
+ // cost, but the values are still hard-coded.
+#define MAX_LIKELY_UPDATE_RATE_HZ 400
+#define MIN_ACCEPTABLE_DITHER_RATE_HZ 50
+#define UPDATES_PER_FULL_DITHER_CYCLE (MAX_LIKELY_UPDATE_RATE_HZ / MIN_ACCEPTABLE_DITHER_RATE_HZ)
+#define RECOMMENDED_VIRTUAL_BITS ((UPDATES_PER_FULL_DITHER_CYCLE>1) + \
+ (UPDATES_PER_FULL_DITHER_CYCLE>2) + \
+ (UPDATES_PER_FULL_DITHER_CYCLE>4) + \
+ (UPDATES_PER_FULL_DITHER_CYCLE>8) + \
+ (UPDATES_PER_FULL_DITHER_CYCLE>16) + \
+ (UPDATES_PER_FULL_DITHER_CYCLE>32) + \
+ (UPDATES_PER_FULL_DITHER_CYCLE>64) + \
+ (UPDATES_PER_FULL_DITHER_CYCLE>128) )
+#define VIRTUAL_BITS RECOMMENDED_VIRTUAL_BITS
+
+ // R is the digther signal 'counter'.
+ static byte R = 0;
+ R++;
+
+ // R is wrapped around at 2^ditherBits,
+ // so if ditherBits is 2, R will cycle through (0,1,2,3)
+ byte ditherBits = VIRTUAL_BITS;
+ R &= (0x01 << ditherBits) - 1;
+
+ // Q is the "unscaled dither signal" itself.
+ // It's initialized to the reversed bits of R.
+ // If 'ditherBits' is 2, Q here will cycle through (0,128,64,192)
+ byte Q = 0;
+
+ // Reverse bits in a byte
+ {
+ if(R & 0x01) { Q |= 0x80; }
+ if(R & 0x02) { Q |= 0x40; }
+ if(R & 0x04) { Q |= 0x20; }
+ if(R & 0x08) { Q |= 0x10; }
+ if(R & 0x10) { Q |= 0x08; }
+ if(R & 0x20) { Q |= 0x04; }
+ if(R & 0x40) { Q |= 0x02; }
+ if(R & 0x80) { Q |= 0x01; }
+ }
+
+ // Now we adjust Q to fall in the center of each range,
+ // instead of at the start of the range.
+ // If ditherBits is 2, Q will be (0, 128, 64, 192) at first,
+ // and this adjustment makes it (31, 159, 95, 223).
+ if( ditherBits < 8) {
+ Q += 0x01 << (7 - ditherBits);
+ }
+
+ // D and E form the "scaled dither signal"
+ // which is added to pixel values to affect the
+ // actual dithering.
+
+ // Setup the initial D and E values
+ for(int i = 0; i < 3; i++) {
+ byte s = mScale.raw[i];
+ e[i] = s ? (256/s) + 1 : 0;
+ d[i] = scale8(Q, e[i]);
+ if(e[i]) e[i]--;
+ }
+#endif
+ }
+
+ // Do we have n pixels left to process?
+ __attribute__((always_inline)) inline bool has(int n) {
+ return mLen >= n;
+ }
+
+ // toggle dithering enable
+ void enable_dithering(EDitherMode dither) {
+ switch(dither) {
+ case BINARY_DITHER: init_binary_dithering(); break;
+ default: d[0]=d[1]=d[2]=e[0]=e[1]=e[2]=0; break;
+ }
+ }
+
+ // get the amount to advance the pointer by
+ __attribute__((always_inline)) inline int advanceBy() { return mAdvance; }
+
+ // advance the data pointer forward, adjust position counter
+ __attribute__((always_inline)) inline void advanceData() { mData += mAdvance; mLen--;}
+
+ // step the dithering forward
+ __attribute__((always_inline)) inline void stepDithering() {
+ // IF UPDATING HERE, BE SURE TO UPDATE THE ASM VERSION IN
+ // clockless_trinket.h!
+ d[0] = e[0] - d[0];
+ d[1] = e[1] - d[1];
+ d[2] = e[2] - d[2];
+ }
+
+ // Some chipsets pre-cycle the first byte, which means we want to cycle byte 0's dithering separately
+ __attribute__((always_inline)) inline void preStepFirstByteDithering() {
+ d[RO(0)] = e[RO(0)] - d[RO(0)];
+ }
+
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadByte(MultiPixelController & pc, int lane) { return pc.mData[pc.mOffsets[lane] + RO(SLOT)]; }
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t dither(MultiPixelController & pc, uint8_t b) { return b ? qadd8(b, pc.d[RO(SLOT)]) : 0; }
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t dither(MultiPixelController & pc, uint8_t b, uint8_t d) { return b ? qadd8(b,d) : 0; }
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t scale(MultiPixelController & pc, uint8_t b) { return scale8(b, pc.mScale.raw[RO(SLOT)]); }
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t scale(MultiPixelController & pc, uint8_t b, uint8_t scale) { return scale8(b, scale); }
+
+ // composite shortcut functions for loading, dithering, and scaling
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(MultiPixelController & pc, int lane) { return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc, lane))); }
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(MultiPixelController & pc, int lane, uint8_t d, uint8_t scale) { return scale8(pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc, lane), d), scale); }
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(MultiPixelController & pc, int lane, uint8_t scale) { return scale8(pc.loadByte<SLOT>(pc, lane), scale); }
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(MultiPixelController & pc, int lane) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc, lane); }
+
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t getd(MultiPixelController & pc) { return pc.d[RO(SLOT)]; }
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t getscale(MultiPixelController & pc) { return pc.mScale.raw[RO(SLOT)]; }
+
+ // Helper functions to get around gcc stupidities
+ __attribute__((always_inline)) inline uint8_t loadAndScale0(int lane) { return loadAndScale<0>(*this, lane); }
+ __attribute__((always_inline)) inline uint8_t loadAndScale1(int lane) { return loadAndScale<1>(*this, lane); }
+ __attribute__((always_inline)) inline uint8_t loadAndScale2(int lane) { return loadAndScale<2>(*this, lane); }
+ __attribute__((always_inline)) inline uint8_t loadAndScale0(int lane, uint8_t d, uint8_t scale) { return loadAndScale<0>(*this, lane, d, scale); }
+ __attribute__((always_inline)) inline uint8_t loadAndScale1(int lane, uint8_t d, uint8_t scale) { return loadAndScale<1>(*this, lane, d, scale); }
+ __attribute__((always_inline)) inline uint8_t loadAndScale2(int lane, uint8_t d, uint8_t scale) { return loadAndScale<2>(*this, lane, d, scale); }
+ __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane) { return advanceAndLoadAndScale<0>(*this, lane); }
+ __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane); }
};
+FASTLED_NAMESPACE_END
+
#endif
diff --git a/dmx.h b/dmx.h
index 8892302f..22379528 100644
--- a/dmx.h
+++ b/dmx.h
@@ -6,6 +6,10 @@
#include<DmxSimple.h>
#define HAS_DMX_SIMPLE
+///@ingroup chipsets
+///@{
+FASTLED_NAMESPACE_BEGIN
+
// note - dmx simple must be included before FastSPI for this code to be enabled
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class DMXSimpleController : public CLEDController {
public:
@@ -49,11 +53,15 @@ protected:
#endif
};
+FASTLED_NAMESPACE_END
+
#endif
#ifdef DmxSerial_h
#include<DMXSerial.h>
+FASTLED_NAMESPACE_BEGIN
+
template <EOrder RGB_ORDER = RGB> class DMXSerialController : public CLEDController {
public:
// initialize the LED controller
@@ -94,6 +102,10 @@ public:
virtual void show(const struct CARGB *data, int nLeds, uint8_t scale = 255) = 0;
#endif
};
+
+FASTLED_NAMESPACE_END
+///@}
+
#define HAS_DMX_SERIAL
#endif
diff --git a/docs/.Doxyfile.swp b/docs/.Doxyfile.swp
new file mode 100644
index 00000000..d223ba7d
--- /dev/null
+++ b/docs/.Doxyfile.swp
Binary files differ
diff --git a/docs/Doxyfile b/docs/Doxyfile
new file mode 100644
index 00000000..aa55681f
--- /dev/null
+++ b/docs/Doxyfile
@@ -0,0 +1,2331 @@
+# Doxyfile 1.8.8
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "FastLED"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = 3.1
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = YES
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = . lib8tion
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE = M0-clocklessnotes.md TODO.md
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html/docs/3.1
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra stylesheet files is of importance (e.g. the last
+# stylesheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string,
+# for the replacement values of the other commands the user is refered to
+# HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = NO
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+PLANTUML_JAR_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/docs/mainpage.dox b/docs/mainpage.dox
new file mode 100644
index 00000000..415bbe4f
--- /dev/null
+++ b/docs/mainpage.dox
@@ -0,0 +1,10 @@
+/**
+@brief Documentation file for FastLED
+@author dgarcia at fastled dot io
+@file
+*/
+/** @defgroup FastLED Sources */
+/**
+@mainpage FastLED - let there be light!
+*/
+EOF
diff --git a/examples/Blink/Blink.ino b/examples/Blink/Blink.ino
index ab307fda..95eb6cb2 100644
--- a/examples/Blink/Blink.ino
+++ b/examples/Blink/Blink.ino
@@ -21,6 +21,7 @@ void setup() {
// FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
+ // FastLED.addLeds<APA104, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS1903B, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<GW6205, DATA_PIN, RGB>(leds, NUM_LEDS);
@@ -29,10 +30,16 @@ void setup() {
// FastLED.addLeds<WS2801, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SM16716, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<LPD8806, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<P9813, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<APA102, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<DOTSTAR, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2801, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SM16716, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<LPD8806, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<P9813, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<DOTSTAR, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
}
void loop() {
diff --git a/examples/ColorPalette/ColorPalette.ino b/examples/ColorPalette/ColorPalette.ino
index 93318998..0fdfb594 100644
--- a/examples/ColorPalette/ColorPalette.ino
+++ b/examples/ColorPalette/ColorPalette.ino
@@ -12,18 +12,18 @@ CRGB leds[NUM_LEDS];
// This example shows several ways to set up and use 'palettes' of colors
// with FastLED.
//
-// These compact palettes provide an easy way to re-colorize your
+// These compact palettes provide an easy way to re-colorize your
// animation on the fly, quickly, easily, and with low overhead.
//
// USING palettes is MUCH simpler in practice than in theory, so first just
-// run this sketch, and watch the pretty lights as you then read through
+// run this sketch, and watch the pretty lights as you then read through
// the code. Although this sketch has eight (or more) different color schemes,
// the entire sketch compiles down to about 6.5K on AVR.
//
// FastLED provides a few pre-configured color palettes, and makes it
// extremely easy to make up your own color schemes with palettes.
//
-// Some notes on the more abstract 'theory and practice' of
+// Some notes on the more abstract 'theory and practice' of
// FastLED compact palettes are at the bottom of this file.
@@ -36,36 +36,36 @@ extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM;
void setup() {
- delay( 3000 ); // power-up safety delay
- FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
- FastLED.setBrightness( BRIGHTNESS );
-
- currentPalette = RainbowColors_p;
- currentBlending = BLEND;
+ delay( 3000 ); // power-up safety delay
+ FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
+ FastLED.setBrightness( BRIGHTNESS );
+
+ currentPalette = RainbowColors_p;
+ currentBlending = LINEARBLEND;
}
void loop()
{
- ChangePalettePeriodically();
-
- static uint8_t startIndex = 0;
- startIndex = startIndex + 1; /* motion speed */
-
- FillLEDsFromPaletteColors( startIndex);
-
- FastLED.show();
- FastLED.delay(1000 / UPDATES_PER_SECOND);
+ ChangePalettePeriodically();
+
+ static uint8_t startIndex = 0;
+ startIndex = startIndex + 1; /* motion speed */
+
+ FillLEDsFromPaletteColors( startIndex);
+
+ FastLED.show();
+ FastLED.delay(1000 / UPDATES_PER_SECOND);
}
void FillLEDsFromPaletteColors( uint8_t colorIndex)
{
- uint8_t brightness = 255;
-
- for( int i = 0; i < NUM_LEDS; i++) {
- leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
- colorIndex += 3;
- }
+ uint8_t brightness = 255;
+
+ for( int i = 0; i < NUM_LEDS; i++) {
+ leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
+ colorIndex += 3;
+ }
}
@@ -79,31 +79,31 @@ void FillLEDsFromPaletteColors( uint8_t colorIndex)
void ChangePalettePeriodically()
{
- uint8_t secondHand = (millis() / 1000) % 60;
- static uint8_t lastSecond = 99;
-
- if( lastSecond != secondHand) {
- lastSecond = secondHand;
- if( secondHand == 0) { currentPalette = RainbowColors_p; currentBlending = BLEND; }
- if( secondHand == 10) { currentPalette = RainbowStripeColors_p; currentBlending = NOBLEND; }
- if( secondHand == 15) { currentPalette = RainbowStripeColors_p; currentBlending = BLEND; }
- if( secondHand == 20) { SetupPurpleAndGreenPalette(); currentBlending = BLEND; }
- if( secondHand == 25) { SetupTotallyRandomPalette(); currentBlending = BLEND; }
- if( secondHand == 30) { SetupBlackAndWhiteStripedPalette(); currentBlending = NOBLEND; }
- if( secondHand == 35) { SetupBlackAndWhiteStripedPalette(); currentBlending = BLEND; }
- if( secondHand == 40) { currentPalette = CloudColors_p; currentBlending = BLEND; }
- if( secondHand == 45) { currentPalette = PartyColors_p; currentBlending = BLEND; }
- if( secondHand == 50) { currentPalette = myRedWhiteBluePalette_p; currentBlending = NOBLEND; }
- if( secondHand == 55) { currentPalette = myRedWhiteBluePalette_p; currentBlending = BLEND; }
- }
+ uint8_t secondHand = (millis() / 1000) % 60;
+ static uint8_t lastSecond = 99;
+
+ if( lastSecond != secondHand) {
+ lastSecond = secondHand;
+ if( secondHand == 0) { currentPalette = RainbowColors_p; currentBlending = LINEARBLEND; }
+ if( secondHand == 10) { currentPalette = RainbowStripeColors_p; currentBlending = NOBLEND; }
+ if( secondHand == 15) { currentPalette = RainbowStripeColors_p; currentBlending = LINEARBLEND; }
+ if( secondHand == 20) { SetupPurpleAndGreenPalette(); currentBlending = LINEARBLEND; }
+ if( secondHand == 25) { SetupTotallyRandomPalette(); currentBlending = LINEARBLEND; }
+ if( secondHand == 30) { SetupBlackAndWhiteStripedPalette(); currentBlending = NOBLEND; }
+ if( secondHand == 35) { SetupBlackAndWhiteStripedPalette(); currentBlending = LINEARBLEND; }
+ if( secondHand == 40) { currentPalette = CloudColors_p; currentBlending = LINEARBLEND; }
+ if( secondHand == 45) { currentPalette = PartyColors_p; currentBlending = LINEARBLEND; }
+ if( secondHand == 50) { currentPalette = myRedWhiteBluePalette_p; currentBlending = NOBLEND; }
+ if( secondHand == 55) { currentPalette = myRedWhiteBluePalette_p; currentBlending = LINEARBLEND; }
+ }
}
// This function fills the palette with totally random colors.
void SetupTotallyRandomPalette()
{
- for( int i = 0; i < 16; i++) {
- currentPalette[i] = CHSV( random8(), 255, random8());
- }
+ for( int i = 0; i < 16; i++) {
+ currentPalette[i] = CHSV( random8(), 255, random8());
+ }
}
// This function sets up a palette of black and white stripes,
@@ -112,55 +112,55 @@ void SetupTotallyRandomPalette()
// to set them up.
void SetupBlackAndWhiteStripedPalette()
{
- // 'black out' all 16 palette entries...
- fill_solid( currentPalette, 16, CRGB::Black);
- // and set every fourth one to white.
- currentPalette[0] = CRGB::White;
- currentPalette[4] = CRGB::White;
- currentPalette[8] = CRGB::White;
- currentPalette[12] = CRGB::White;
-
+ // 'black out' all 16 palette entries...
+ fill_solid( currentPalette, 16, CRGB::Black);
+ // and set every fourth one to white.
+ currentPalette[0] = CRGB::White;
+ currentPalette[4] = CRGB::White;
+ currentPalette[8] = CRGB::White;
+ currentPalette[12] = CRGB::White;
+
}
// This function sets up a palette of purple and green stripes.
void SetupPurpleAndGreenPalette()
{
- CRGB purple = CHSV( HUE_PURPLE, 255, 255);
- CRGB green = CHSV( HUE_GREEN, 255, 255);
- CRGB black = CRGB::Black;
-
- currentPalette = CRGBPalette16(
- green, green, black, black,
- purple, purple, black, black,
- green, green, black, black,
- purple, purple, black, black );
+ CRGB purple = CHSV( HUE_PURPLE, 255, 255);
+ CRGB green = CHSV( HUE_GREEN, 255, 255);
+ CRGB black = CRGB::Black;
+
+ currentPalette = CRGBPalette16(
+ green, green, black, black,
+ purple, purple, black, black,
+ green, green, black, black,
+ purple, purple, black, black );
}
// This example shows how to set up a static color palette
-// which is stored in PROGMEM (flash), which is almost always more
+// which is stored in PROGMEM (flash), which is almost always more
// plentiful than RAM. A static PROGMEM palette like this
// takes up 64 bytes of flash.
const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM =
{
- CRGB::Red,
- CRGB::Gray, // 'white' is too bright compared to red and blue
- CRGB::Blue,
- CRGB::Black,
-
- CRGB::Red,
- CRGB::Gray,
- CRGB::Blue,
- CRGB::Black,
-
- CRGB::Red,
- CRGB::Red,
- CRGB::Gray,
- CRGB::Gray,
- CRGB::Blue,
- CRGB::Blue,
- CRGB::Black,
- CRGB::Black
+ CRGB::Red,
+ CRGB::Gray, // 'white' is too bright compared to red and blue
+ CRGB::Blue,
+ CRGB::Black,
+
+ CRGB::Red,
+ CRGB::Gray,
+ CRGB::Blue,
+ CRGB::Black,
+
+ CRGB::Red,
+ CRGB::Red,
+ CRGB::Gray,
+ CRGB::Gray,
+ CRGB::Blue,
+ CRGB::Blue,
+ CRGB::Black,
+ CRGB::Black
};
@@ -171,7 +171,7 @@ const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM =
// has 256 entries, each containing a specific 24-bit RGB color. You can then
// index into the color palette using a simple 8-bit (one byte) value.
// A 256-entry color palette takes up 768 bytes of RAM, which on Arduino
-// is quite possibly "too many" bytes.
+// is quite possibly "too many" bytes.
//
// FastLED does offer traditional 256-element palettes, for setups that
// can afford the 768-byte cost in RAM.
@@ -179,11 +179,10 @@ const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM =
// However, FastLED also offers a compact alternative. FastLED offers
// palettes that store 16 distinct entries, but can be accessed AS IF
// they actually have 256 entries; this is accomplished by interpolating
-// between the 16 explicit entries to create fifteen intermediate palette
+// between the 16 explicit entries to create fifteen intermediate palette
// entries between each pair.
//
// So for example, if you set the first two explicit entries of a compact
// palette to Green (0,255,0) and Blue (0,0,255), and then retrieved
// the first sixteen entries from the virtual palette (of 256), you'd get
// Green, followed by a smooth gradient from green-to-blue, and then Blue.
-
diff --git a/examples/Cylon/Cylon.ino b/examples/Cylon/Cylon.ino
index dfb6ed78..e484adb3 100644
--- a/examples/Cylon/Cylon.ino
+++ b/examples/Cylon/Cylon.ino
@@ -1,43 +1,53 @@
#include "FastLED.h"
// How many leds in your strip?
-#define NUM_LEDS 40
+#define NUM_LEDS 64
// For led chips like Neopixels, which have a data line, ground, and power, you just
// need to define DATA_PIN. For led chipsets that are SPI based (four wires - data, clock,
// ground, and power), like the LPD8806, define both DATA_PIN and CLOCK_PIN
-#define DATA_PIN 6
+#define DATA_PIN 7
#define CLOCK_PIN 13
// Define the array of leds
CRGB leds[NUM_LEDS];
void setup() {
- FastLED.addLeds<NEOPIXEL,DATA_PIN>(leds, NUM_LEDS);
+ Serial.begin(57600);
+ Serial.println("resetting");
+ LEDS.addLeds<WS2812,2,RGB>(leds,NUM_LEDS);
+ LEDS.setBrightness(84);
}
+void fadeall() { for(int i = 0; i < NUM_LEDS; i++) { leds[i].nscale8(250); } }
+
void loop() {
+ static uint8_t hue = 0;
+ Serial.print("x");
// First slide the led in one direction
for(int i = 0; i < NUM_LEDS; i++) {
// Set the i'th led to red
- leds[i] = CRGB::Red;
+ leds[i] = CHSV(hue++, 255, 255);
// Show the leds
- FastLED.show();
+ FastLED.show();
// now that we've shown the leds, reset the i'th led to black
- leds[i] = CRGB::Black;
+ // leds[i] = CRGB::Black;
+ fadeall();
// Wait a little bit before we loop around and do it again
- delay(30);
+ delay(10);
}
+ Serial.print("x");
// Now go in the other direction.
- for(int i = NUM_LEDS-1; i >= 0; i--) {
+ for(int i = (NUM_LEDS)-1; i >= 0; i--) {
// Set the i'th led to red
- leds[i] = CRGB::Red;
+ leds[i] = CHSV(hue++, 255, 255);
// Show the leds
FastLED.show();
// now that we've shown the leds, reset the i'th led to black
- leds[i] = CRGB::Black;
+ // leds[i] = CRGB::Black;
+ fadeall();
// Wait a little bit before we loop around and do it again
- delay(30);
+ delay(10);
}
}
diff --git a/examples/DemoReel100/DemoReel100.ino b/examples/DemoReel100/DemoReel100.ino
new file mode 100644
index 00000000..3f686943
--- /dev/null
+++ b/examples/DemoReel100/DemoReel100.ino
@@ -0,0 +1,126 @@
+#include "FastLED.h"
+
+FASTLED_USING_NAMESPACE
+
+// FastLED "100-lines-of-code" demo reel, showing just a few
+// of the kinds of animation patterns you can quickly and easily
+// compose using FastLED.
+//
+// This example also shows one easy way to define multiple
+// animations patterns and have them automatically rotate.
+//
+// -Mark Kriegsman, December 2014
+
+#if FASTLED_VERSION < 3001000
+#error "Requires FastLED 3.1 or later; check github for latest code."
+#endif
+
+#define DATA_PIN 3
+//#define CLK_PIN 4
+#define LED_TYPE WS2811
+#define COLOR_ORDER GRB
+#define NUM_LEDS 64
+CRGB leds[NUM_LEDS];
+
+#define BRIGHTNESS 96
+#define FRAMES_PER_SECOND 120
+
+void setup() {
+ delay(3000); // 3 second delay for recovery
+
+ // tell FastLED about the LED strip configuration
+ FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
+ //FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
+
+ // set master brightness control
+ FastLED.setBrightness(BRIGHTNESS);
+}
+
+
+// List of patterns to cycle through. Each is defined as a separate function below.
+typedef void (*SimplePatternList[])();
+SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm };
+
+uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
+uint8_t gHue = 0; // rotating "base color" used by many of the patterns
+
+void loop()
+{
+ // Call the current pattern function once, updating the 'leds' array
+ gPatterns[gCurrentPatternNumber]();
+
+ // send the 'leds' array out to the actual LED strip
+ FastLED.show();
+ // insert a delay to keep the framerate modest
+ FastLED.delay(1000/FRAMES_PER_SECOND);
+
+ // do some periodic updates
+ EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the "base color" through the rainbow
+ EVERY_N_SECONDS( 10 ) { nextPattern(); } // change patterns periodically
+}
+
+#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
+
+void nextPattern()
+{
+ // add one to the current pattern number, and wrap around at the end
+ gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE( gPatterns);
+}
+
+void rainbow()
+{
+ // FastLED's built-in rainbow generator
+ fill_rainbow( leds, NUM_LEDS, gHue, 7);
+}
+
+void rainbowWithGlitter()
+{
+ // built-in FastLED rainbow, plus some random sparkly glitter
+ rainbow();
+ addGlitter(80);
+}
+
+void addGlitter( fract8 chanceOfGlitter)
+{
+ if( random8() < chanceOfGlitter) {
+ leds[ random16(NUM_LEDS) ] += CRGB::White;
+ }
+}
+
+void confetti()
+{
+ // random colored speckles that blink in and fade smoothly
+ fadeToBlackBy( leds, NUM_LEDS, 10);
+ int pos = random16(NUM_LEDS);
+ leds[pos] += CHSV( gHue + random8(64), 200, 255);
+}
+
+void sinelon()
+{
+ // a colored dot sweeping back and forth, with fading trails
+ fadeToBlackBy( leds, NUM_LEDS, 20);
+ int pos = beatsin16(13,0,NUM_LEDS);
+ leds[pos] += CHSV( gHue, 255, 192);
+}
+
+void bpm()
+{
+ // colored stripes pulsing at a defined Beats-Per-Minute (BPM)
+ uint8_t BeatsPerMinute = 62;
+ CRGBPalette16 palette = PartyColors_p;
+ uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
+ for( int i = 0; i < NUM_LEDS; i++) { //9948
+ leds[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10));
+ }
+}
+
+void juggle() {
+ // eight colored dots, weaving in and out of sync with each other
+ fadeToBlackBy( leds, NUM_LEDS, 20);
+ byte dothue = 0;
+ for( int i = 0; i < 8; i++) {
+ leds[beatsin16(i+7,0,NUM_LEDS)] |= CHSV(dothue, 200, 255);
+ dothue += 32;
+ }
+}
+
diff --git a/examples/Fire2012/Fire2012.ino b/examples/Fire2012/Fire2012.ino
index 0929e2f2..dec5cd7f 100644
--- a/examples/Fire2012/Fire2012.ino
+++ b/examples/Fire2012/Fire2012.ino
@@ -8,6 +8,8 @@
#define BRIGHTNESS 200
#define FRAMES_PER_SECOND 60
+bool gReverseDirection = false;
+
CRGB leds[NUM_LEDS];
void setup() {
@@ -19,7 +21,7 @@ void setup() {
void loop()
{
// Add entropy to random number generator; we use a lot of it.
- random16_add_entropy( random());
+ // random16_add_entropy( random());
Fire2012(); // run simulation frame
@@ -90,7 +92,14 @@ void Fire2012()
// Step 4. Map from heat cells to LED colors
for( int j = 0; j < NUM_LEDS; j++) {
- leds[j] = HeatColor( heat[j]);
+ CRGB color = HeatColor( heat[j]);
+ int pixelnumber;
+ if( gReverseDirection ) {
+ pixelnumber = (NUM_LEDS-1) - j;
+ } else {
+ pixelnumber = j;
+ }
+ leds[pixelnumber] = color;
}
}
diff --git a/examples/Fire2012WithPalette/Fire2012WithPalette.ino b/examples/Fire2012WithPalette/Fire2012WithPalette.ino
index 41b9a247..e65a87fb 100644
--- a/examples/Fire2012WithPalette/Fire2012WithPalette.ino
+++ b/examples/Fire2012WithPalette/Fire2012WithPalette.ino
@@ -8,6 +8,8 @@
#define BRIGHTNESS 200
#define FRAMES_PER_SECOND 60
+bool gReverseDirection = false;
+
CRGB leds[NUM_LEDS];
// Fire2012 with programmable Color Palette
@@ -149,7 +151,14 @@ void Fire2012WithPalette()
// Scale the heat value from 0-255 down to 0-240
// for best results with color palettes.
byte colorindex = scale8( heat[j], 240);
- leds[j] = ColorFromPalette( gPal, colorindex);
+ CRGB color = ColorFromPalette( gPal, colorindex);
+ int pixelnumber;
+ if( gReverseDirection ) {
+ pixelnumber = (NUM_LEDS-1) - j;
+ } else {
+ pixelnumber = j;
+ }
+ leds[pixelnumber] = color;
}
}
diff --git a/examples/FirstLight/FirstLight.ino b/examples/FirstLight/FirstLight.ino
index 928c7bbd..ce89f640 100644
--- a/examples/FirstLight/FirstLight.ino
+++ b/examples/FirstLight/FirstLight.ino
@@ -1,8 +1,8 @@
// Use if you want to force the software SPI subsystem to be used for some reason (generally, you don't)
-// #define FORCE_SOFTWARE_SPI
+// #define FASTLED_FORCE_SOFTWARE_SPI
// Use if you want to force non-accelerated pin access (hint: you really don't, it breaks lots of things)
-// #define FORCE_SOFTWARE_SPI
-// #define FORCE_SOFTWARE_PINS
+// #define FASTLED_FORCE_SOFTWARE_SPI
+// #define FASTLED_FORCE_SOFTWARE_PINS
#include "FastLED.h"
///////////////////////////////////////////////////////////////////////////////////////////
@@ -36,6 +36,7 @@ void setup() {
// FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
+ // FastLED.addLeds<APA104, DATA_PIN>(leds, NUM_LEDS);
// FastLED.addLeds<WS2811_400, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<GW6205, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<GW6205_400, DATA_PIN, RGB>(leds, NUM_LEDS);
@@ -46,10 +47,15 @@ void setup() {
// FastLED.addLeds<SM16716, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<LPD8806, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<P9813, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<APA102, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<DOTSTAR, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2801, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SM16716, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<LPD8806, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<P9813, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<DOTSTAR, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
}
// This function runs over and over, and is where you do the magic to light
diff --git a/examples/Multiple/OctoWS2811Demo/OctoWS2811Demo.ino b/examples/Multiple/OctoWS2811Demo/OctoWS2811Demo.ino
new file mode 100644
index 00000000..6aad445d
--- /dev/null
+++ b/examples/Multiple/OctoWS2811Demo/OctoWS2811Demo.ino
@@ -0,0 +1,37 @@
+#define USE_OCTOWS2811
+#include<OctoWS2811.h>
+#include<FastLED.h>
+
+#define NUM_LEDS_PER_STRIP 64
+#define NUM_STRIPS 8
+
+CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP];
+
+// Pin layouts on the teensy 3:
+// OctoWS2811: 2,14,7,8,6,20,21,5
+
+void setup() {
+ LEDS.addLeds<OCTOWS2811>(leds, NUM_LEDS_PER_STRIP);
+ LEDS.setBrightness(32);
+}
+
+void loop() {
+ static uint8_t hue = 0;
+ for(int i = 0; i < NUM_STRIPS; i++) {
+ for(int j = 0; j < NUM_LEDS_PER_STRIP; j++) {
+ leds[(i*NUM_LEDS_PER_STRIP) + j] = CHSV((32*i) + hue+j,192,255);
+ }
+ }
+
+ // Set the first n leds on each strip to show which strip it is
+ for(int i = 0; i < NUM_STRIPS; i++) {
+ for(int j = 0; j <= i; j++) {
+ leds[(i*NUM_LEDS_PER_STRIP) + j] = CRGB::Red;
+ }
+ }
+
+ hue++;
+
+ LEDS.show();
+ LEDS.delay(10);
+}
diff --git a/examples/Multiple/ParallelOutputDemo/ParallelOutputDemo.ino b/examples/Multiple/ParallelOutputDemo/ParallelOutputDemo.ino
new file mode 100644
index 00000000..d646cdec
--- /dev/null
+++ b/examples/Multiple/ParallelOutputDemo/ParallelOutputDemo.ino
@@ -0,0 +1,47 @@
+#include<FastLED.h>
+
+#define NUM_LEDS_PER_STRIP 64
+// Note: this can be 12 if you're using a teensy 3 and don't mind soldering the pads on the back
+#define NUM_STRIPS 16
+
+CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP];
+
+// Pin layouts on the teensy 3/3.1:
+// WS2811_PORTD: 2,14,7,8,6,20,21,5
+// WS2811_PORTC: 15,22,23,9,10,13,11,12,28,27,29,30 (these last 4 are pads on the bottom of the teensy)
+// WS2811_PORTDC: 2,14,7,8,6,20,21,5,15,22,23,9,10,13,11,12 - 16 way parallel
+//
+// Pin layouts on the due
+// WS2811_PORTA: 69,68,61,60,59,100,58,31 (note: pin 100 only available on the digix)
+// WS2811_PORTB: 90,91,92,93,94,95,96,97 (note: only available on the digix)
+// WS2811_PORTD: 25,26,27,28,14,15,29,11
+//
+
+void setup() {
+ // LEDS.addLeds<WS2811_PORTA,NUM_STRIPS>(leds, NUM_LEDS_PER_STRIP);
+ // LEDS.addLeds<WS2811_PORTB,NUM_STRIPS>(leds, NUM_LEDS_PER_STRIP);
+ // LEDS.addLeds<WS2811_PORTD,NUM_STRIPS>(leds, NUM_LEDS_PER_STRIP).setCorrection(TypicalLEDStrip);
+ LEDS.addLeds<WS2811_PORTDC,NUM_STRIPS>(leds, NUM_LEDS_PER_STRIP);
+ LEDS.setBrightness(32);
+}
+
+void loop() {
+ static uint8_t hue = 0;
+ for(int i = 0; i < NUM_STRIPS; i++) {
+ for(int j = 0; j < NUM_LEDS_PER_STRIP; j++) {
+ leds[(i*NUM_LEDS_PER_STRIP) + j] = CHSV((32*i) + hue+j,192,255);
+ }
+ }
+
+ // Set the first n leds on each strip to show which strip it is
+ for(int i = 0; i < NUM_STRIPS; i++) {
+ for(int j = 0; j <= i; j++) {
+ leds[(i*NUM_LEDS_PER_STRIP) + j] = CRGB::Red;
+ }
+ }
+
+ hue++;
+
+ LEDS.show();
+ LEDS.delay(10);
+}
diff --git a/examples/Ports/PJRCSpectrumAnalyzer/PJRCSpectrumAnalyzer.ino b/examples/Ports/PJRCSpectrumAnalyzer/PJRCSpectrumAnalyzer.ino
new file mode 100644
index 00000000..24f2394e
--- /dev/null
+++ b/examples/Ports/PJRCSpectrumAnalyzer/PJRCSpectrumAnalyzer.ino
@@ -0,0 +1,136 @@
+// LED Audio Spectrum Analyzer Display
+//
+// Creates an impressive LED light show to music input
+// using Teensy 3.1 with the OctoWS2811 adaptor board
+// http://www.pjrc.com/store/teensy31.html
+// http://www.pjrc.com/store/octo28_adaptor.html
+//
+// Line Level Audio Input connects to analog pin A3
+// Recommended input circuit:
+// http://www.pjrc.com/teensy/gui/?info=AudioInputAnalog
+//
+// This example code is in the public domain.
+
+#define USE_OCTOWS2811
+#include <OctoWS2811.h>
+#include <FastLED.h>
+#include <Audio.h>
+#include <Wire.h>
+#include <SD.h>
+#include <SPI.h>
+
+// The display size and color to use
+const unsigned int matrix_width = 60;
+const unsigned int matrix_height = 32;
+const unsigned int myColor = 0x400020;
+
+// These parameters adjust the vertical thresholds
+const float maxLevel = 0.5; // 1.0 = max, lower is more "sensitive"
+const float dynamicRange = 40.0; // total range to display, in decibels
+const float linearBlend = 0.3; // useful range is 0 to 0.7
+
+CRGB leds[matrix_width * matrix_height];
+
+// Audio library objects
+AudioInputAnalog adc1(A3); //xy=99,55
+AudioAnalyzeFFT1024 fft; //xy=265,75
+AudioConnection patchCord1(adc1, fft);
+
+
+// This array holds the volume level (0 to 1.0) for each
+// vertical pixel to turn on. Computed in setup() using
+// the 3 parameters above.
+float thresholdVertical[matrix_height];
+
+// This array specifies how many of the FFT frequency bin
+// to use for each horizontal pixel. Because humans hear
+// in octaves and FFT bins are linear, the low frequencies
+// use a small number of bins, higher frequencies use more.
+int frequencyBinsHorizontal[matrix_width] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
+ 3, 3, 3, 3, 4, 4, 4, 4, 4, 5,
+ 5, 5, 6, 6, 6, 7, 7, 7, 8, 8,
+ 9, 9, 10, 10, 11, 12, 12, 13, 14, 15,
+ 15, 16, 17, 18, 19, 20, 22, 23, 24, 25
+};
+
+
+
+// Run setup once
+void setup() {
+ // the audio library needs to be given memory to start working
+ AudioMemory(12);
+
+ // compute the vertical thresholds before starting
+ computeVerticalLevels();
+
+ // turn on the display
+ FastLED.addLeds<OCTOWS2811>(leds,(matrix_width * matrix_height) / 8);
+}
+
+// A simple xy() function to turn display matrix coordinates
+// into the index numbers OctoWS2811 requires. If your LEDs
+// are arranged differently, edit this code...
+unsigned int xy(unsigned int x, unsigned int y) {
+ if ((y & 1) == 0) {
+ // even numbered rows (0, 2, 4...) are left to right
+ return y * matrix_width + x;
+ } else {
+ // odd numbered rows (1, 3, 5...) are right to left
+ return y * matrix_width + matrix_width - 1 - x;
+ }
+}
+
+// Run repetitively
+void loop() {
+ unsigned int x, y, freqBin;
+ float level;
+
+ if (fft.available()) {
+ // freqBin counts which FFT frequency data has been used,
+ // starting at low frequency
+ freqBin = 0;
+
+ for (x=0; x < matrix_width; x++) {
+ // get the volume for each horizontal pixel position
+ level = fft.read(freqBin, freqBin + frequencyBinsHorizontal[x] - 1);
+
+ // uncomment to see the spectrum in Arduino's Serial Monitor
+ // Serial.print(level);
+ // Serial.print(" ");
+
+ for (y=0; y < matrix_height; y++) {
+ // for each vertical pixel, check if above the threshold
+ // and turn the LED on or off
+ if (level >= thresholdVertical[y]) {
+ leds[xy(x,y)] = CRGB(myColor);
+ } else {
+ leds[xy(x,y)] = CRGB::Black;
+ }
+ }
+ // increment the frequency bin count, so we display
+ // low to higher frequency from left to right
+ freqBin = freqBin + frequencyBinsHorizontal[x];
+ }
+ // after all pixels set, show them all at the same instant
+ FastLED.show();
+ // Serial.println();
+ }
+}
+
+
+// Run once from setup, the compute the vertical levels
+void computeVerticalLevels() {
+ unsigned int y;
+ float n, logLevel, linearLevel;
+
+ for (y=0; y < matrix_height; y++) {
+ n = (float)y / (float)(matrix_height - 1);
+ logLevel = pow10f(n * -1.0 * (dynamicRange / 20.0));
+ linearLevel = 1.0 - n;
+ linearLevel = linearLevel * linearBlend;
+ logLevel = logLevel * (1.0 - linearBlend);
+ thresholdVertical[y] = (logLevel + linearLevel) * maxLevel;
+ }
+}
diff --git a/extras/AppleII.s65 b/extras/AppleII.s65
new file mode 100644
index 00000000..98e4e4b1
--- /dev/null
+++ b/extras/AppleII.s65
@@ -0,0 +1,40 @@
+KBD = $C000 ;Read keydown
+KBDSTRB = $C010 ;Reset keybd
+
+SPKR = $C030 ;Toggle speaker
+
+; TTL digital output pins on
+; 16-pin DIP game connector
+SETAN0 = $C058
+CLRAN0 = $C059
+SETAN1 = $C05A
+CLRAN1 = $C05B
+SETAN2 = $C05C
+CLRAN2 = $C05D
+SETAN3 = $C05E
+CLRAN3 = $C05F
+
+PIN12ON = CLRAN3
+PIN12OFF= SETAN3
+PIN13ON = CLRAN2
+PIN13OFF= SETAN2
+PIN14ON = CLRAN1
+PIN14OFF= SETAN1
+PIN15ON = CLRAN0
+PIN15OFF= SETAN0
+
+;Special for pin 5, except on //gs
+C040STROBE = $C040
+PIN5STROBE = C040STROBE
+
+SolidApple = $C062 ; read SW1 or SA
+OpenApple = $C061 ; read SW0 or OA
+
+VBL = $C019 ; vertical blanking
+
+WAIT = $FCA8 ; wait a little while
+
+CROUT = $FD8E ; print a CR
+PRBYTE = $FDDA ; print a hex byte
+
+
diff --git a/extras/FastLED6502.s65 b/extras/FastLED6502.s65
new file mode 100644
index 00000000..3d1a4fe6
--- /dev/null
+++ b/extras/FastLED6502.s65
@@ -0,0 +1,633 @@
+/////////////////////////////////
+//
+// FastLED6502
+// by Mark Kriegsman
+//
+// Device driver and animation
+// library for connecting addressable
+// LED strips to an Apple II.
+// The full "FastLED" library is
+// available for Arduino and related
+// microcontrollers.
+//
+/////////////////////////////////
+//
+// HOST COMPATIBILITY:
+// Apples with 16-pin DIP "game ports"
+// are fully supported; Apples with only
+// 9-pin "game ports" are NOT supported.
+//
+// Apple ][ fully supported
+// Apple ][+ fully supported
+// Apple //e fully supported
+//
+// Apple //c and //c+ NOT supported
+// as they lack the required 16-pin
+// DIP game port for digital I/O.
+//
+// Apple //gs:
+// motherboard game port IS supported
+// back panel connector NOT supported
+// See Notes section below.
+//
+// C64, PET, VIC-20, Atari400/800,
+// NES, SYM, KIM-1, and other 6502
+// systems are NOT supported at this
+// time, but porting should be
+// relatively easy for someone familiar
+// with each target platform.
+//
+//
+// LED STRIP COMPATIBILITY:
+// In general, "four-wire" (clocked)
+// LED strips can be supported, but
+// "three-wire" (clockless) LED strips
+// cannot be supported.
+//
+// APA102 tested & working
+// Adafruit DotStar tested & working
+// LPD8806 should work (untested)
+// WS2801 should work (untested)
+//
+// WS2811/WS2812/NeoPixel can NOT work
+// due to timing incompatibilities,
+// namely that the 1MHz Apple is far
+// too slow to drive them correctly.
+//
+//
+// USAGE - HARDWARE:
+// Connect an external power source to
+// +5 and GND on the LED strip.
+// Connect GND on the LED strip to GND
+// on the game port connector (pin 8)
+// Connect DATA IN on the LED strip to
+// pin 12, 13, or 14 on the game port.
+// Connect CLOCK IN on the LED strip to
+// pin 5 (preferred), 12, 13, or 14 on
+// the game port. Note: Apple //gs users
+// cannot use pin 5.
+//
+//
+// USAGE - SOFTWARE:
+// At build time provide definitions for
+// CHIPSET, DATA_PIN, CLOCK_PIN,
+// NUM_LEDS, & BRIGHTNESS.
+// Inside "Setup":
+// Store LED count into
+// FastLED_NumPixels,
+// Store brightness into
+// FastLED_Brightness
+// Inside "Loop":
+// Update the leds array, found in
+// ledsR, ledsG, and ledsB
+// Call FastLED_Show
+// Jump back to top of "Loop"
+//
+//
+// REFERENCE:
+// FastLED_Show:
+// display led array on LED strip
+// FastLED_FillBlack:
+// fill led array to black
+// FastLED_FillSolid_RGB_AXY:
+// fill led array with RGB color
+// FastLED_FillSolid_Hue_X:
+// fill led array with solid HSV Hue
+// FastLED_Random8:
+// return a pseudorandom number in A
+// FastLED_FillRainbow_XY:
+// fill led array with HSV rainbow
+// starting at hue X, incrementing by
+// huedelta Y each pixel.
+// FastLED_SetHue_XY:
+// set pixel Y to HSV hue X
+// FastLED_Beat8:
+// takes a speed increment in A, and
+// returns a triangle wave in A.
+//
+//
+// NOTES:
+// For speed and size, there IS some
+// self-modifying code in this
+// library, so it cannot be burned
+// into ROM without modification.
+// Brightness control only works on
+// APA102 at this point.
+// Pin 15 is currently used as a
+// 'frame start' signal for protocol
+// diagnostics - makes frames more
+// visible with an oscilloscope.
+// If Pin 5 is specified for the CLOCK
+// output pin, FastLED6502 will
+// automatically use the high speed
+// C040STROBE signal for clock. Note
+// that the Apple //gs lacks this
+// signal, even on the motherboard
+// game port.
+// The Apple joystick/mouse port on the
+// rear of the //c, //c+, and //gs
+// can NOT be used for LED connections
+// because it lacks the necessary
+// digital output pins.
+// This library can drive 100 LED pixels
+// at more than 30 frames per second.
+//
+//
+// VERSION HISTORY
+// 2015-02-07 - first version, by MEK
+// assembled with xa65
+// www.floodgap.com/retrotech/xa/
+
+
+/////////////////////////////////
+//
+// ENTRY POINT
+//
+
+FastLED_Entry
+ jsr FastLED_FillBlack
+ jmp Setup
+
+
+/////////////////////////////////
+//
+// FASTLED6502 GLOBALS
+//
+
+FastLED_NumPixels .byt NUM_LEDS
+FastLED_Brightness .byt BRIGHTNESS
+
+FastLED_RandomState .byt 17
+FastLED_BeatState .byt 0
+
+
+/////////////////////////////////
+//
+// API FUNCTIONS
+//
+
+FastLED_FillBlack
+ lda FastLED_NumPixels
+ pha
+ lda #255
+ sta FastLED_NumPixels
+ lda #0
+ tax
+ tay
+ jsr FastLED_FillSolid_RGB_AXY
+ jsr FastLED_Show
+ pla
+ sta FastLED_NumPixels
+ rts
+
+
+FastLED_FillRainbow_XY
+ sty rbHueDelta
+ ldy #0
+FR1
+ lda FastLED_RainbowR,x
+ sta ledsR,y
+ lda FastLED_RainbowG,x
+ sta ledsG,y
+ lda FastLED_RainbowB,x
+ sta ledsB,y
+
+ txa
+ clc
+ adc rbHueDelta
+ tax
+
+ iny
+ cpy FastLED_NumPixels
+ bne FR1
+ rts
+
+rbHueDelta .byt 0
+
+
+FastLED_SetHue_XY
+ lda FastLED_RainbowR,x
+ sta ledsR,y
+ lda FastLED_RainbowG,x
+ sta ledsG,y
+ lda FastLED_RainbowB,x
+ sta ledsB,y
+ rts
+
+
+FastLED_FillSolid_RGB_AXY
+ sta ledsR
+ stx ledsG
+ sty ledsB
+ ldy #0
+FillSolidRGBAXY1
+ lda ledsR
+ sta ledsR,y
+ lda ledsG
+ sta ledsG,y
+ lda ledsB
+ sta ledsB,y
+ iny
+ cpy FastLED_NumPixels
+ bne FillSolidRGBAXY1
+ rts
+
+FastLED_FillSolid_Hue_X
+ ldy #0
+FillSolidHX1
+ lda FastLED_RainbowR,x
+ sta ledsR,y
+ lda FastLED_RainbowG,x
+ sta ledsG,y
+ lda FastLED_RainbowB,x
+ sta ledsB,y
+ iny
+ cpy FastLED_NumPixels
+ bne FillSolidHX1
+ rts
+
+
+; NOTE: USES SELF-MODIFYING CODE
+FastLED_Random8
+ inc Random8GetLo
+ bne Random8Get
+ inc Random8GetHi
+ bne Random8Get
+ lda #$F8
+ sta Random8GetHi
+ lda #03
+ sta Random8GetLo
+Random8Get
+Random8GetLo = Random8Get + 1
+Random8GetHi = Random8Get + 2
+ lda $F803
+ adc FastLED_RandomState
+ sta FastLED_RandomState
+ rts
+
+
+FastLED_Beat8
+ clc
+ adc FastLED_BeatState
+ sta FastLED_BeatState
+ bit FastLED_BeatState
+ bmi FastLED_Beat8Neg
+ asl
+ rts
+FastLED_Beat8Neg
+ lda #$ff
+ sec
+ sbc FastLED_BeatState
+ sbc FastLED_BeatState
+ rts
+
+
+FastLED_Show
+ jmp CHIPSET
+
+
+/////////////////////////////////
+//
+// HARDWARE INTERFACING
+//
+
+PINOFF_BASE = PIN15OFF
+PINON_BASE = PIN15ON
+
+#define PINON(P) PINON_BASE+((15-P)*2)
+#define PINOFF(P) PINOFF_BASE+((15-P)*2)
+
+DATAOFF = PINOFF(DATA_PIN)
+DATAON = PINON(DATA_PIN)
+CLKOFF = PINOFF(CLOCK_PIN)
+CLKON = PINON(CLOCK_PIN)
+
+// Special handling if CLOCK_PIN
+// is 5: the C040STROBE line.
+#if CLOCK_PIN = 5
+#define CLOCK_ON bit PIN5STROBE
+#define CLOCK_OFF
+#else
+#define CLOCK_ON bit CLKON
+#define CLOCK_OFF bit CLKOFF
+#endif
+
+FRAMEON = PINON(15)
+FRAMEOFF = PINOFF(15)
+
+/////////////////////////////////
+APA102
+ bit FRAMEON
+ jsr FastLED_Send00
+ jsr FastLED_Send00
+ jsr FastLED_Send00
+ jsr FastLED_Send00
+
+ lda FastLED_Brightness
+ lsr
+ lsr
+ lsr
+ ora #$E0
+ tax
+
+ ldy FastLED_NumPixels
+APA102PX
+ txa
+ jsr FastLED_SendA
+ lda ledsB,y
+ jsr FastLED_SendA
+ lda ledsG,y
+ jsr FastLED_SendA
+ lda ledsR,y
+ jsr FastLED_SendA
+ dey
+ bne APA102PX
+
+ lda FastLED_NumPixels
+ lsr
+ lsr
+ lsr
+ lsr
+ lsr
+ lsr
+ tay
+ iny
+APA102CL
+ jsr FastLED_SendFF
+ jsr FastLED_Send00
+ jsr FastLED_Send00
+ jsr FastLED_Send00
+ dey
+ bne APA102CL
+ bit FRAMEOFF
+ rts
+
+/////////////////////////////////
+LPD8806
+ bit FRAMEON
+ ldy FastLED_NumPixels
+LPD8806PX
+ lda ledsG,y
+ lsr
+ ora #$80
+ jsr FastLED_SendA
+ lda ledsR,y
+ lsr
+ ora #$80
+ jsr FastLED_SendA
+ lda ledsB,y
+ lsr
+ ora #$80
+ jsr FastLED_SendA
+ dey
+ bne LPD8806PX
+
+ bit FRAMEOFF
+ rts
+
+
+/////////////////////////////////
+WS2801
+ bit FRAMEON
+ ldy FastLED_NumPixels
+WS2801PX
+ lda ledsG,y
+ jsr FastLED_SendA
+ lda ledsR,y
+ jsr FastLED_SendA
+ lda ledsB,y
+ jsr FastLED_SendA
+ dey
+ bne WS2801PX
+
+ bit FRAMEOFF
+ rts
+
+
+/////////////////////////////////
+FastLED_SendFF
+ bit DATAON
+ jmp FastLED_SendXX
+ ;
+FastLED_Send00
+ bit DATAOFF
+ ;
+FastLED_SendXX
+ CLOCK_ON
+ CLOCK_OFF
+ CLOCK_ON
+ CLOCK_OFF
+
+ CLOCK_ON
+ CLOCK_OFF
+ CLOCK_ON
+ CLOCK_OFF
+
+ CLOCK_ON
+ CLOCK_OFF
+ CLOCK_ON
+ CLOCK_OFF
+
+ CLOCK_ON
+ CLOCK_OFF
+ CLOCK_ON
+ CLOCK_OFF
+
+ rts
+
+FastLED_SendA
+ cmp #0
+ beq FastLED_Send00
+ cmp #$FF
+ beq FastLED_SendFF
+
+ asl
+ bcc S0x0
+S0x1 bit DATAON
+ bcs S0xK
+S0x0 bit DATAOFF
+S0xK CLOCK_ON
+ CLOCK_OFF
+
+ asl
+ bcc S1x0
+S1x1 bit DATAON
+ bcs S1xK
+S1x0 bit DATAOFF
+S1xK CLOCK_ON
+ CLOCK_OFF
+
+ asl
+ bcc S2x0
+S2x1 bit DATAON
+ bcs S2xK
+S2x0 bit DATAOFF
+S2xK CLOCK_ON
+ CLOCK_OFF
+
+ asl
+ bcc S3x0
+S3x1 bit DATAON
+ bcs S3xK
+S3x0 bit DATAOFF
+S3xK CLOCK_ON
+ CLOCK_OFF
+
+ asl
+ bcc S4x0
+S4x1 bit DATAON
+ bcs S4xK
+S4x0 bit DATAOFF
+S4xK CLOCK_ON
+ CLOCK_OFF
+
+ asl
+ bcc S5x0
+S5x1 bit DATAON
+ bcs S5xK
+S5x0 bit DATAOFF
+S5xK CLOCK_ON
+ CLOCK_OFF
+
+ asl
+ bcc S6x0
+S6x1 bit DATAON
+ bcs S6xK
+S6x0 bit DATAOFF
+S6xK CLOCK_ON
+ CLOCK_OFF
+
+ asl
+ bcc S7x0
+S7x1 bit DATAON
+ bcs S7xK
+S7x0 bit DATAOFF
+S7xK CLOCK_ON
+ CLOCK_OFF
+
+ rts
+
+
+/////////////////////////////////
+//
+// Force page allignment for speed
+// for leds array and Rainbow table
+//
+ .dsb 256-(* & $FF),0
+
+
+/////////////////////////////////
+//
+// LED ARRAY
+//
+
+ledsR .dsb 256,0
+ledsG .dsb 256,0
+ledsB .dsb 256,0
+
+/////////////////////////////////
+//
+// HSV RAINBOW DEFINITION
+//
+// Generated directly from FastLED.
+//
+
+FastLED_RainbowR
+ .byt $FF,$FD,$FA,$F8,$F5,$F2,$F0,$ED
+ .byt $EA,$E8,$E5,$E2,$E0,$DD,$DA,$D8
+ .byt $D5,$D2,$D0,$CD,$CA,$C8,$C5,$C2
+ .byt $C0,$BD,$BA,$B8,$B5,$B2,$B0,$AD
+ .byt $AB,$AB,$AB,$AB,$AB,$AB,$AB,$AB
+ .byt $AB,$AB,$AB,$AB,$AB,$AB,$AB,$AB
+ .byt $AB,$AB,$AB,$AB,$AB,$AB,$AB,$AB
+ .byt $AB,$AB,$AB,$AB,$AB,$AB,$AB,$AB
+ .byt $AB,$A6,$A1,$9C,$96,$91,$8C,$86
+ .byt $81,$7C,$76,$71,$6C,$66,$61,$5C
+ .byt $56,$51,$4C,$47,$41,$3C,$37,$31
+ .byt $2C,$27,$21,$1C,$17,$11,$0C,$07
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$02,$05,$07,$0A,$0D,$0F,$12
+ .byt $15,$17,$1A,$1D,$1F,$22,$25,$27
+ .byt $2A,$2D,$2F,$32,$35,$37,$3A,$3D
+ .byt $3F,$42,$45,$47,$4A,$4D,$4F,$52
+ .byt $55,$57,$5A,$5C,$5F,$62,$64,$67
+ .byt $6A,$6C,$6F,$72,$74,$77,$7A,$7C
+ .byt $7F,$82,$84,$87,$8A,$8C,$8F,$92
+ .byt $94,$97,$9A,$9C,$9F,$A2,$A4,$A7
+ .byt $AB,$AD,$B0,$B2,$B5,$B8,$BA,$BD
+ .byt $C0,$C2,$C5,$C8,$CA,$CD,$D0,$D2
+ .byt $D5,$D8,$DA,$DD,$E0,$E2,$E5,$E8
+ .byt $EA,$ED,$F0,$F2,$F5,$F8,$FA,$FD
+FastLED_RainbowG
+ .byt $00,$02,$05,$07,$0A,$0D,$0F,$12
+ .byt $15,$17,$1A,$1D,$1F,$22,$25,$27
+ .byt $2A,$2D,$2F,$32,$35,$37,$3A,$3D
+ .byt $3F,$42,$45,$47,$4A,$4D,$4F,$52
+ .byt $55,$57,$5A,$5C,$5F,$62,$64,$67
+ .byt $6A,$6C,$6F,$72,$74,$77,$7A,$7C
+ .byt $7F,$82,$84,$87,$8A,$8C,$8F,$92
+ .byt $94,$97,$9A,$9C,$9F,$A2,$A4,$A7
+ .byt $AB,$AD,$B0,$B2,$B5,$B8,$BA,$BD
+ .byt $C0,$C2,$C5,$C8,$CA,$CD,$D0,$D2
+ .byt $D5,$D8,$DA,$DD,$E0,$E2,$E5,$E8
+ .byt $EA,$ED,$F0,$F2,$F5,$F8,$FA,$FD
+ .byt $FF,$FD,$FA,$F8,$F5,$F2,$F0,$ED
+ .byt $EA,$E8,$E5,$E2,$E0,$DD,$DA,$D8
+ .byt $D5,$D2,$D0,$CD,$CA,$C8,$C5,$C2
+ .byt $C0,$BD,$BA,$B8,$B5,$B2,$B0,$AD
+ .byt $AB,$A6,$A1,$9C,$96,$91,$8C,$86
+ .byt $81,$7C,$76,$71,$6C,$66,$61,$5C
+ .byt $56,$51,$4C,$47,$41,$3C,$37,$31
+ .byt $2C,$27,$21,$1C,$17,$11,$0C,$07
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+FastLED_RainbowB
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$00,$00,$00,$00,$00,$00,$00
+ .byt $00,$02,$05,$07,$0A,$0D,$0F,$12
+ .byt $15,$17,$1A,$1D,$1F,$22,$25,$27
+ .byt $2A,$2D,$2F,$32,$35,$37,$3A,$3D
+ .byt $3F,$42,$45,$47,$4A,$4D,$4F,$52
+ .byt $55,$5A,$5F,$64,$6A,$6F,$74,$7A
+ .byt $7F,$84,$8A,$8F,$94,$9A,$9F,$A4
+ .byt $AA,$AF,$B4,$B9,$BF,$C4,$C9,$CF
+ .byt $D4,$D9,$DF,$E4,$E9,$EF,$F4,$F9
+ .byt $FF,$FD,$FA,$F8,$F5,$F2,$F0,$ED
+ .byt $EA,$E8,$E5,$E2,$E0,$DD,$DA,$D8
+ .byt $D5,$D2,$D0,$CD,$CA,$C8,$C5,$C2
+ .byt $C0,$BD,$BA,$B8,$B5,$B2,$B0,$AD
+ .byt $AB,$A9,$A6,$A4,$A1,$9E,$9C,$99
+ .byt $96,$94,$91,$8E,$8C,$89,$86,$84
+ .byt $81,$7E,$7C,$79,$76,$74,$71,$6E
+ .byt $6C,$69,$66,$64,$61,$5E,$5C,$59
+ .byt $55,$53,$50,$4E,$4B,$48,$46,$43
+ .byt $40,$3E,$3B,$38,$36,$33,$30,$2E
+ .byt $2B,$28,$26,$23,$20,$1E,$1B,$18
+ .byt $16,$13,$10,$0E,$0B,$08,$06,$03
diff --git a/extras/RainbowDemo.bin.zip b/extras/RainbowDemo.bin.zip
new file mode 100644
index 00000000..853c38e2
--- /dev/null
+++ b/extras/RainbowDemo.bin.zip
Binary files differ
diff --git a/extras/RainbowDemo.s65 b/extras/RainbowDemo.s65
new file mode 100644
index 00000000..2de6233b
--- /dev/null
+++ b/extras/RainbowDemo.s65
@@ -0,0 +1,89 @@
+; "Rainbow with glitter" demo
+; for "FastLED6502"
+;
+; Runs on an Apple ][, ][+, //e, or //gs
+;
+; Supports APA102, Adafruit DotStar,
+; LPD8806, and WS2801 LED strips.
+;
+; LED strip connects to game port pins,
+; see FastLED6502.s65 for details.
+;
+; Mark Kriegsman, February 2015
+
+#define NUM_LEDS 100
+#define BRIGHTNESS 64
+#define CHIPSET APA102
+#define DATA_PIN 14
+#define CLOCK_PIN 5
+
+ * = $6000
+
+#include "FastLED6502.s65"
+#include "AppleII.s65"
+
+gHue .byt 0
+gHueDelta .byt 17
+gHueSpeed .byt 7
+
+
+Setup
+ lda #0
+ sta gHue
+
+Loop
+ lda gHue
+ clc
+ adc gHueSpeed
+ sta gHue
+ ldx gHue
+ ldy gHueDelta
+; Fill RGB array with HSV rainbow
+ jsr FastLED_FillRainbow_XY
+; Use master brightness control
+ lda #BRIGHTNESS
+ sta FastLED_Brightness
+CheckOpenApple
+ bit OpenApple
+ bpl CheckSolidApple
+; Add glitter if requested
+ jsr AddGlitter
+CheckSolidApple
+ bit SolidApple
+ bpl DoDisplay
+; Pulse brightness if requested
+ jsr PulseBrightness
+DoDisplay
+; This is where the magic happens
+ jsr FastLED_Show
+ jmp Loop
+
+
+AddGlitter
+ ldy #3
+MaybeAdd1Glitter
+ jsr FastLED_Random8
+ cmp FastLED_NumPixels
+ bcs SkipThis1Glitter
+ tax
+ lda #$FF
+ sta ledsR,x
+ sta ledsG,x
+ sta ledsB,x
+SkipThis1Glitter
+ dey
+ bne MaybeAdd1Glitter
+ rts
+
+
+PulseBrightness
+ lda #13
+ jsr FastLED_Beat8
+ clc
+ adc #12
+ bcc PulseBright1
+ lda #$FF
+PulseBright1
+ sta FastLED_Brightness
+ rts
+ \ No newline at end of file
diff --git a/fastled_config.h b/fastled_config.h
new file mode 100644
index 00000000..42023f53
--- /dev/null
+++ b/fastled_config.h
@@ -0,0 +1,22 @@
+#ifndef __INC_FASTLED_CONFIG_H
+#define __INC_FASTLED_CONFIG_H
+
+///@file fastled_config.h
+/// contains definitions that can be used to configure FastLED at compile time
+
+// Use this option only for debugging pin access and forcing software pin access. Note that
+// software pin access only works in Arduino based environments. Forces use of digitalWrite
+// methods for pin access vs. direct hardware port access
+// #define FASTLED_FORCE_SOFTWARE_PINS
+
+// Use this option only for debugging bitbang'd spi access or to work around bugs in hardware
+// spi access. Forces use of bit-banged spi, even on pins that has hardware SPI available.
+// #define FASTLED_FORCE_SOFTWARE_SPI
+
+// Use this to force FastLED to allow interrupts in the clockless chipsets (or to force it to
+// disallow), overriding the default on platforms that support this. Set the value to 1 to
+// allow interrupts or 0 to disallow them.
+// #define FASTLED_ALLOW_INTERRUPTS 1
+// #define FASTLED_ALLOW_INTERRUPTS 0
+
+#endif
diff --git a/delay.h b/fastled_delay.h
index 05313127..5df7609d 100644
--- a/delay.h
+++ b/fastled_delay.h
@@ -1,17 +1,22 @@
-#ifndef __INC_DELAY_H
-#define __INC_DELAY_H
+#ifndef __INC_FL_DELAY_H
+#define __INC_FL_DELAY_H
-// Class to ensure that a minimum amount of time has kicked since the last time run - and delay if not enough time has passed yet
-// this should make sure that chipsets that have
+///@file fastled_delay.h
+///Utility functions and classes for managing delaycycles
+
+FASTLED_NAMESPACE_BEGIN
+
+/// Class to ensure that a minimum amount of time has kicked since the last time run - and delay if not enough time has passed yet
+/// this should make sure that chipsets that have
template<int WAIT> class CMinWait {
uint16_t mLastMicros;
public:
CMinWait() { mLastMicros = 0; }
- void wait() {
+ void wait() {
uint16_t diff;
do {
- diff = (micros() & 0xFFFF) - mLastMicros;
+ diff = (micros() & 0xFFFF) - mLastMicros;
} while(diff < WAIT);
}
@@ -25,12 +30,13 @@ public:
//
////////////////////////////////////////////////////////////////////////////////////////////
-#if defined(__arm__)
-# define NOP __asm__ __volatile__ ("nop\n");
-# define NOP2 __asm__ __volatile__ ("nop\n\tnop");
-#else
+// Default is now just 'nop', with special case for AVR
+#if defined(__AVR__)
# define NOP __asm__ __volatile__ ("cp r0,r0\n");
# define NOP2 __asm__ __volatile__ ("rjmp .+0");
+#else
+# define NOP __asm__ __volatile__ ("nop\n");
+# define NOP2 __asm__ __volatile__ ("nop\n\t nop\n");
#endif
// predeclaration to not upset the compiler
@@ -41,42 +47,42 @@ template<int CYCLES> inline void delaycycles();
// usable definition
#if defined(FASTLED_AVR)
// worker template - this will nop for LOOP * 3 + PAD cycles total
-template<int LOOP, int PAD> inline void _delaycycles_AVR() {
+template<int LOOP, int PAD> inline void _delaycycles_AVR() {
delaycycles<PAD>();
// the loop below is 3 cycles * LOOP. the LDI is one cycle,
// the DEC is 1 cycle, the BRNE is 2 cycles if looping back and
// 1 if not (the LDI balances out the BRNE being 1 cycle on exit)
- __asm__ __volatile__ (
+ __asm__ __volatile__ (
" LDI R16, %0\n"
"L_%=: DEC R16\n"
" BRNE L_%=\n"
- : /* no outputs */
- : "M" (LOOP)
+ : /* no outputs */
+ : "M" (LOOP)
: "r16"
);
}
-template<int CYCLES> __attribute__((always_inline)) inline void delaycycles() {
- _delaycycles_AVR<CYCLES / 3, CYCLES % 3>();
+template<int CYCLES> __attribute__((always_inline)) inline void delaycycles() {
+ _delaycycles_AVR<CYCLES / 3, CYCLES % 3>();
}
#else
-// template<int LOOP, int PAD> inline void _delaycycles_ARM() {
+// template<int LOOP, int PAD> inline void _delaycycles_ARM() {
// delaycycles<PAD>();
// // the loop below is 3 cycles * LOOP. the LDI is one cycle,
// // the DEC is 1 cycle, the BRNE is 2 cycles if looping back and
// // 1 if not (the LDI balances out the BRNE being 1 cycle on exit)
-// __asm__ __volatile__ (
+// __asm__ __volatile__ (
// " mov.w r9, %0\n"
// "L_%=: subs.w r9, r9, #1\n"
// " bne.n L_%=\n"
-// : /* no outputs */
-// : "M" (LOOP)
+// : /* no outputs */
+// : "M" (LOOP)
// : "r9"
// );
// }
-template<int CYCLES> __attribute__((always_inline)) inline void delaycycles() {
+template<int CYCLES> __attribute__((always_inline)) inline void delaycycles() {
// _delaycycles_ARM<CYCLES / 3, CYCLES % 3>();
NOP; delaycycles<CYCLES-1>();
}
@@ -97,63 +103,19 @@ template<> __attribute__((always_inline)) inline void delaycycles<3>() {NOP;NOP2
template<> __attribute__((always_inline)) inline void delaycycles<4>() {NOP2;NOP2;}
template<> __attribute__((always_inline)) inline void delaycycles<5>() {NOP2;NOP2;NOP;}
-// Some timing related macros/definitions
+// Some timing related macros/definitions
// Macro to convert from nano-seconds to clocks and clocks to nano-seconds
// #define NS(_NS) (_NS / (1000 / (F_CPU / 1000000L)))
-#if 1 || (F_CPU < 96000000)
-#define NS(_NS) ( (_NS * (F_CPU / 1000000L))) / 1000
+#define F_CPU_MHZ (F_CPU / 1000000L)
+
+// #define NS(_NS) ( (_NS * (F_CPU / 1000000L))) / 1000
+#define NS(_NS) (((_NS * F_CPU_MHZ) + 999) / 1000)
#define CLKS_TO_MICROS(_CLKS) ((long)(_CLKS)) / (F_CPU / 1000000L)
-#else
-#define NS(_NS) ( (_NS * (F_CPU / 2000000L))) / 1000
-#define CLKS_TO_MICROS(_CLKS) ((long)(_CLKS)) / (F_CPU / 2000000L)
-#endif
// Macro for making sure there's enough time available
#define NO_TIME(A, B, C) (NS(A) < 3 || NS(B) < 3 || NS(C) < 6)
-
-#if defined(FASTLED_TEENSY3)
- extern volatile uint32_t systick_millis_count;
-# define MS_COUNTER systick_millis_count
-#elif defined(__SAM3X8E__)
- extern volatile uint32_t fuckit;
-# define MS_COUNTER fuckit
-#else
-# if defined(CORE_TEENSY)
- extern volatile unsigned long timer0_millis_count;
-# define MS_COUNTER timer0_millis_count
-# else
- extern volatile unsigned long timer0_millis;
-# define MS_COUNTER timer0_millis
-# endif
-#endif
-
-#ifdef __SAM3X8E__
-class SysClockSaver {
- SysTick_Type m_Saved;
-public:
- SysClockSaver(int newTimeValue) { save(newTimeValue); }
- void save(int newTimeValue) {
- m_Saved.CTRL = SysTick->CTRL;
- SysTick->CTRL &= ~(SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk);
- m_Saved.LOAD = SysTick->LOAD;
- m_Saved.VAL = SysTick->VAL;
-
- SysTick->VAL = 0;
- SysTick->LOAD = newTimeValue;
- SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk;
- SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
- }
-
- void restore() {
- SysTick->CTRL &= ~(SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk);
- SysTick->LOAD = m_Saved.LOAD;
- SysTick->VAL = m_Saved.VAL;
- SysTick->CTRL = m_Saved.CTRL;
- }
-};
-
-#endif
+FASTLED_NAMESPACE_END
#endif
diff --git a/fastled_progmem.h b/fastled_progmem.h
new file mode 100644
index 00000000..f7cedc02
--- /dev/null
+++ b/fastled_progmem.h
@@ -0,0 +1,64 @@
+#ifndef __INC_FL_PROGMEM_H
+#define __INC_FL_PROGMEM_H
+
+///@file fastled_progmem.h
+/// wrapper definitions to allow seamless use of PROGMEM in environmens that have it
+
+FASTLED_NAMESPACE_BEGIN
+
+// Compatibility layer for devices that do or don't
+// have "PROGMEM" and the associated pgm_ accessors.
+//
+// If a platform supports PROGMEM, it should define
+// "FASTLED_USE_PROGMEM" as 1, otherwise FastLED will
+// fall back to NOT using PROGMEM.
+//
+// Whether or not pgmspace.h is #included is separately
+// controllable by FASTLED_INCLUDE_PGMSPACE, if needed.
+
+
+// If FASTLED_USE_PROGMEM is 1, we'll map FL_PROGMEM
+// and the FL_PGM_* accessors to the Arduino equivalents.
+#if FASTLED_USE_PROGMEM == 1
+#ifndef FASTLED_INCLUDE_PGMSPACE
+#define FASTLED_INCLUDE_PGMSPACE 1
+#endif
+
+#if FASTLED_INCLUDE_PGMSPACE == 1
+#include <avr/pgmspace.h>
+#endif
+
+#define FL_PROGMEM PROGMEM
+
+// Note: only the 'near' memory wrappers are provided.
+// If you're using 'far' memory, you already have
+// portability issues to work through, but you could
+// add more support here if needed.
+#define FL_PGM_READ_BYTE_NEAR(x) (pgm_read_byte_near(x))
+#define FL_PGM_READ_WORD_NEAR(x) (pgm_read_word_near(x))
+#define FL_PGM_READ_DWORD_NEAR(x) (pgm_read_dword_near(x))
+
+// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734
+#ifdef FASTLED_AVR
+#ifdef PROGMEM
+#undef PROGMEM
+#define PROGMEM __attribute__((section(".progmem.data")))
+#endif
+#endif
+
+#else
+// If FASTLED_USE_PROGMEM is 0 or undefined,
+// we'll use regular memory (RAM) access.
+
+//empty PROGMEM simulation
+#define FL_PROGMEM
+#define FL_PGM_READ_BYTE_NEAR(x) (*((const uint8_t*)(x)))
+#define FL_PGM_READ_WORD_NEAR(x) (*((const uint16_t*)(x)))
+#define FL_PGM_READ_DWORD_NEAR(x) (*((const uint32_t*)(x)))
+
+#endif
+
+
+FASTLED_NAMESPACE_END
+
+#endif
diff --git a/fastpin.h b/fastpin.h
index 56a9ea61..b7fa666e 100644
--- a/fastpin.h
+++ b/fastpin.h
@@ -3,7 +3,12 @@
#include "led_sysdefs.h"
-#define NO_PIN 255
+///@file fastpin.h
+/// Class base definitions for defining fast pin access
+
+FASTLED_NAMESPACE_BEGIN
+
+#define NO_PIN 255
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
@@ -18,13 +23,15 @@ public:
virtual bool isSelected() = 0;
};
-class Pin : public Selectable {
+#if !defined(FASTLED_NO_PINMAP)
+
+class Pin : public Selectable {
volatile RwReg *mPort;
volatile RoReg *mInPort;
RwReg mPinMask;
uint8_t mPin;
- void _init() {
+ void _init() {
mPinMask = digitalPinToBitMask(mPin);
mPort = portOutputRegister(digitalPinToPort(mPin));
mInPort = portInputRegister(digitalPinToPort(mPin));
@@ -38,14 +45,14 @@ public:
inline void setOutput() { pinMode(mPin, OUTPUT); }
inline void setInput() { pinMode(mPin, INPUT); }
- inline void hi() __attribute__ ((always_inline)) { *mPort |= mPinMask; }
+ inline void hi() __attribute__ ((always_inline)) { *mPort |= mPinMask; }
inline void lo() __attribute__ ((always_inline)) { *mPort &= ~mPinMask; }
inline void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline void toggle() __attribute__ ((always_inline)) { *mInPort = mPinMask; }
- inline void hi(register port_ptr_t port) __attribute__ ((always_inline)) { *port |= mPinMask; }
- inline void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~mPinMask; }
+ inline void hi(register port_ptr_t port) __attribute__ ((always_inline)) { *port |= mPinMask; }
+ inline void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~mPinMask; }
inline void set(register port_t val) __attribute__ ((always_inline)) { *mPort = val; }
inline void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
@@ -70,28 +77,90 @@ public:
InputPin(int pin) : Pin(pin) { setInput(); }
};
+#else
+// This is the empty code version of the raw pin class, method bodies should be filled in to Do The Right Thing[tm] when making this
+// available on a new platform
+class Pin : public Selectable {
+ volatile RwReg *mPort;
+ volatile RoReg *mInPort;
+ RwReg mPinMask;
+ uint8_t mPin;
+
+ void _init() {
+ // TODO: fill in init on a new platform
+ mPinMask = 0;
+ mPort = NULL;
+ mInPort = NULL;
+ }
+public:
+ Pin(int pin) : mPin(pin) { _init(); }
+
+ void setPin(int pin) { mPin = pin; _init(); }
+
+ typedef volatile RwReg * port_ptr_t;
+ typedef RwReg port_t;
+
+ inline void setOutput() { /* TODO: Set pin output */ }
+ inline void setInput() { /* TODO: Set pin input */ }
+
+ inline void hi() __attribute__ ((always_inline)) { *mPort |= mPinMask; }
+ inline void lo() __attribute__ ((always_inline)) { *mPort &= ~mPinMask; }
+
+ inline void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
+ inline void toggle() __attribute__ ((always_inline)) { *mInPort = mPinMask; }
+
+ inline void hi(register port_ptr_t port) __attribute__ ((always_inline)) { *port |= mPinMask; }
+ inline void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~mPinMask; }
+ inline void set(register port_t val) __attribute__ ((always_inline)) { *mPort = val; }
+
+ inline void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
+
+ port_t hival() __attribute__ ((always_inline)) { return *mPort | mPinMask; }
+ port_t loval() __attribute__ ((always_inline)) { return *mPort & ~mPinMask; }
+ port_ptr_t port() __attribute__ ((always_inline)) { return mPort; }
+ port_t mask() __attribute__ ((always_inline)) { return mPinMask; }
+
+ virtual void select() { hi(); }
+ virtual void release() { lo(); }
+ virtual bool isSelected() { return (*mPort & mPinMask) == mPinMask; }
+};
+
+class OutputPin : public Pin {
+public:
+ OutputPin(int pin) : Pin(pin) { setOutput(); }
+};
+
+class InputPin : public Pin {
+public:
+ InputPin(int pin) : Pin(pin) { setInput(); }
+};
+
+#endif
+
/// The simplest level of Pin class. This relies on runtime functions durinig initialization to get the port/pin mask for the pin. Most
/// of the accesses involve references to these static globals that get set up. This won't be the fastest set of pin operations, but it
/// will provide pin level access on pretty much all arduino environments. In addition, it includes some methods to help optimize access in
/// various ways. Namely, the versions of hi, lo, and fastset that take the port register as a passed in register variable (saving a global
/// dereference), since these functions are aggressively inlined, that can help collapse out a lot of extraneous memory loads/dereferences.
-///
+///
/// In addition, if, while writing a bunch of data to a pin, you know no other pins will be getting written to, you can get/cache a value of
/// the pin's port register and use that to do a full set to the register. This results in one being able to simply do a store to the register,
/// vs. the load, and/or, and store that would be done normally.
///
/// There are platform specific instantiations of this class that provide direct i/o register access to pins for much higher speed pin twiddling.
///
-/// Note that these classes are all static functions. So the proper usage is Pin<13>::hi(); or such. Instantiating objects is not recommended,
+/// Note that these classes are all static functions. So the proper usage is Pin<13>::hi(); or such. Instantiating objects is not recommended,
/// as passing Pin objects around will likely -not- have the effect you're expecting.
-template<uint8_t PIN> class FastPin {
+template<uint8_t PIN> class FastPin {
static RwReg sPinMask;
static volatile RwReg *sPort;
static volatile RoReg *sInPort;
- static void _init() {
+ static void _init() {
+#if !defined(FASTLED_NO_PINMAP)
sPinMask = digitalPinToBitMask(PIN);
sPort = portOutputRegister(digitalPinToPort(PIN));
sInPort = portInputRegister(digitalPinToPort(PIN));
+#endif
}
public:
typedef volatile RwReg * port_ptr_t;
@@ -100,15 +169,15 @@ public:
inline static void setOutput() { _init(); pinMode(PIN, OUTPUT); }
inline static void setInput() { _init(); pinMode(PIN, INPUT); }
- inline static void hi() __attribute__ ((always_inline)) { *sPort |= sPinMask; }
+ inline static void hi() __attribute__ ((always_inline)) { *sPort |= sPinMask; }
inline static void lo() __attribute__ ((always_inline)) { *sPort &= ~sPinMask; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline static void toggle() __attribute__ ((always_inline)) { *sInPort = sPinMask; }
- inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { *port |= sPinMask; }
- inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~sPinMask; }
+ inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { *port |= sPinMask; }
+ inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~sPinMask; }
inline static void set(register port_t val) __attribute__ ((always_inline)) { *sPort = val; }
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
@@ -125,36 +194,9 @@ template<uint8_t PIN> volatile RoReg *FastPin<PIN>::sInPort;
template<uint8_t PIN> class FastPinBB : public FastPin<PIN> {};
-
typedef volatile uint32_t & reg32_t;
typedef volatile uint32_t * ptr_reg32_t;
+FASTLED_NAMESPACE_END
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// Pin definitions for AVR and ARM. If there are pin definitions supplied below for the platform being
-// built on, then much higher speed access will be possible, namely with direct GPIO register accesses.
-//
-///////////////////////////////////////////////////////////////////////////////////////////////////////////
-#if defined(FORCE_SOFTWARE_PINS)
-
-#warning "Softwrae pin support forced pin access will be slightly slower. See fastpin.h for info."
-#define NO_HARDWARE_PIN_SUPPORT
-#undef HAS_HARDWARE_PIN_SUPPORT
-
-#else
-
-// We want hardware pin support, include the hardware pin header files
-#include "fastpin_avr.h"
-#include "fastpin_arm_k20.h"
-#include "fastpin_arm_sam.h"
-
-#ifndef HAS_HARDWARE_PIN_SUPPORT
-#warning "No pin/port mappings found, pin access will be slightly slower. See fastpin.h for info."
-#define NO_HARDWARE_PIN_SUPPORT
-#endif
-
-#endif
-
-#endif
+#endif // __INC_FASTPIN_H
diff --git a/fastspi.h b/fastspi.h
index f80956f4..500e3e0c 100644
--- a/fastspi.h
+++ b/fastspi.h
@@ -3,55 +3,19 @@
#include "controller.h"
#include "lib8tion.h"
-#include "delay.h"
-
-// Some helper macros for getting at mis-ordered byte values
-#define SPI_B0 (RGB_BYTE0(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
-#define SPI_B1 (RGB_BYTE1(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
-#define SPI_B2 (RGB_BYTE2(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
-#define SPI_ADVANCE (3 + (MASK_SKIP_BITS & SKIP))
-
-/// Some of the SPI controllers will need to perform a transform on each byte before doing
-/// anyting with it. Creating a class of this form and passing it in as a template parameter to
-/// writeBytes/writeBytes3 below will ensure that the body of this method will get called on every
-/// byte worked on. Recommendation, make the adjust method aggressively inlined.
-///
-/// TODO: Convinience macro for building these
-class DATA_NOP {
-public:
- static __attribute__((always_inline)) inline uint8_t adjust(register uint8_t data) { return data; }
- static __attribute__((always_inline)) inline uint8_t adjust(register uint8_t data, register uint8_t scale) { return scale8(data, scale); }
- static __attribute__((always_inline)) inline void postBlock(int len) {}
-};
-
-#define FLAG_START_BIT 0x80
-#define MASK_SKIP_BITS 0x3F
-
-// Clock speed dividers
-#define SPEED_DIV_2 2
-#define SPEED_DIV_4 4
-#define SPEED_DIV_8 8
-#define SPEED_DIV_16 16
-#define SPEED_DIV_32 32
-#define SPEED_DIV_64 64
-#define SPEED_DIV_128 128
-
-#define MAX_DATA_RATE 0
-#if (CLK_DBL == 1)
-#define DATA_RATE_MHZ(X) (((F_CPU / 1000000L) / X)/2)
-#define DATA_RATE_KHZ(X) (((F_CPU / 1000L) / X)/2)
+
+#include "fastspi_bitbang.h"
+
+FASTLED_NAMESPACE_BEGIN
+
+#if defined(FASTLED_TEENSY3) && (F_CPU > 48000000)
+#define DATA_RATE_MHZ(X) (((48000000L / 1000000L) / X))
+#define DATA_RATE_KHZ(X) (((48000000L / 1000L) / X))
#else
#define DATA_RATE_MHZ(X) ((F_CPU / 1000000L) / X)
#define DATA_RATE_KHZ(X) ((F_CPU / 1000L) / X)
#endif
-// Include the various specific SPI implementations
-#include "fastspi_bitbang.h"
-#include "fastspi_arm_k20.h"
-#include "fastspi_arm_sam.h"
-#include "fastspi_avr.h"
-#include "fastspi_dma.h"
-
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// External SPI template definition with partial instantiation(s) to map to hardware SPI ports on platforms/builds where the pin
@@ -65,10 +29,10 @@ class SPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
class SoftwareSPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
-#ifndef FORCE_SOFTWARE_SPI
+#ifndef FASTLED_FORCE_SOFTWARE_SPI
#if defined(SPI_DATA) && defined(SPI_CLOCK)
-#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
+#if defined(FASTLED_TEENSY3) && defined(ARM_HARDWARE_SPI)
template<uint8_t SPI_SPEED>
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED, 0x4002C000> {};
@@ -77,18 +41,57 @@ class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SP
template<uint8_t SPI_SPEED>
class SPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED, 0x4002C000> {};
+
+template<uint8_t SPI_SPEED>
+class SPIOutput<SPI_DATA, SPI2_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI_DATA, SPI2_CLOCK, SPI_SPEED, 0x4002C000> {};
+
+template<uint8_t SPI_SPEED>
+class SPIOutput<SPI2_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI2_DATA, SPI_CLOCK, SPI_SPEED, 0x4002C000> {};
#endif
+#elif defined(FASTLED_TEENSYLC) && defined(ARM_HARDWARE_SPI)
+
+#define DECLARE_SPI0(__DATA,__CLOCK) template<uint8_t SPI_SPEED>\
+ class SPIOutput<__DATA, __CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<__DATA, __CLOCK, SPI_SPEED, 0x40076000> {};
+ #define DECLARE_SPI1(__DATA,__CLOCK) template<uint8_t SPI_SPEED>\
+ class SPIOutput<__DATA, __CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<__DATA, __CLOCK, SPI_SPEED, 0x40077000> {};
+
+DECLARE_SPI0(7,13);
+DECLARE_SPI0(8,13);
+DECLARE_SPI0(11,13);
+DECLARE_SPI0(12,13);
+DECLARE_SPI0(7,14);
+DECLARE_SPI0(8,14);
+DECLARE_SPI0(11,14);
+DECLARE_SPI0(12,14);
+DECLARE_SPI1(0,20);
+DECLARE_SPI1(1,20);
+DECLARE_SPI1(21,20);
+
#elif defined(__SAM3X8E__)
template<uint8_t SPI_SPEED>
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public SAMHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> {};
-#else
+#elif defined(AVR_HARDWARE_SPI)
template<uint8_t SPI_SPEED>
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public AVRHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> {};
+#if defined(SPI_UART0_DATA)
+
+template<uint8_t SPI_SPEED>
+class SPIOutput<SPI_UART0_DATA, SPI_UART0_CLOCK, SPI_SPEED> : public AVRUSART0SPIOutput<SPI_UART0_DATA, SPI_UART0_CLOCK, SPI_SPEED> {};
+
+#endif
+
+#if defined(SPI_UART1_DATA)
+
+template<uint8_t SPI_SPEED>
+class SPIOutput<SPI_UART1_DATA, SPI_UART1_CLOCK, SPI_SPEED> : public AVRUSART1SPIOutput<SPI_UART1_DATA, SPI_UART1_CLOCK, SPI_SPEED> {};
+
+#endif
+
#endif
#else
@@ -105,4 +108,6 @@ class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public AVRHardwareSPIOutput<SP
#warning "Forcing software SPI - no hardware SPI for you!"
#endif
+FASTLED_NAMESPACE_END
+
#endif
diff --git a/fastspi_bitbang.h b/fastspi_bitbang.h
index b596f628..65c2a5d8 100644
--- a/fastspi_bitbang.h
+++ b/fastspi_bitbang.h
@@ -1,6 +1,10 @@
#ifndef __INC_FASTSPI_BITBANG_H
#define __INC_FASTSPI_BITBANG_H
+#include "fastled_delay.h"
+
+FASTLED_NAMESPACE_BEGIN
+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Software SPI (aka bit-banging) support - with aggressive optimizations for when the clock and data pin are on the same port
@@ -112,20 +116,12 @@ public:
template <uint8_t BIT> __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) {
if(b & (1 << BIT)) {
FastPin<DATA_PIN>::hi();
- if(SPI_SPEED < 3) {
- FastPin<CLOCK_PIN>::strobe();
- } else {
- FastPin<CLOCK_PIN>::hi(); SPI_DELAY;
- FastPin<CLOCK_PIN>::lo(); SPI_DELAY;
- }
+ FastPin<CLOCK_PIN>::hi(); SPI_DELAY;
+ FastPin<CLOCK_PIN>::lo(); SPI_DELAY;
} else {
FastPin<DATA_PIN>::lo();
- if(SPI_SPEED < 3) {
- FastPin<CLOCK_PIN>::strobe();
- } else {
- FastPin<CLOCK_PIN>::hi(); SPI_DELAY;
- FastPin<CLOCK_PIN>::lo(); SPI_DELAY;
- }
+ FastPin<CLOCK_PIN>::hi(); SPI_DELAY;
+ FastPin<CLOCK_PIN>::lo(); SPI_DELAY;
}
}
@@ -353,4 +349,6 @@ public:
}
};
+FASTLED_NAMESPACE_END
+
#endif
diff --git a/fastspi_nop.h b/fastspi_nop.h
index 64cd5128..aa363394 100644
--- a/fastspi_nop.h
+++ b/fastspi_nop.h
@@ -1,63 +1,59 @@
#ifndef __INC_FASTSPI_NOP_H
#define __INC_FASTSPI_NOP_H
-// A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should
-// be used as a definition for the set of methods that the spi implementation classes should use (since C++ doesn't support the
-// idea of interfaces - it's possible this could be done with virtual classes, need to decide if i want that overhead)
+FASTLED_NAMESPACE_BEGIN
+
+/// A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should
+/// be used as a definition for the set of methods that the spi implementation classes should use (since C++ doesn't support the
+/// idea of interfaces - it's possible this could be done with virtual classes, need to decide if i want that overhead)
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
class NOPSPIOutput {
+ Selectable *m_pSelect;
public:
- NOPSPIOutput() { /* TODO */ }
- NOPSPIOutput(Selectable *pSelect) { /* TODO */ }
+ NOPSPIOutput() { m_pSelect = NULL; }
+ NOPSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
- // set the object representing the selectable
- void setSelect(Selectable *pSelect) { /* TODO */ }
+ /// set the object representing the selectable
+ void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
- // initialize the SPI subssytem
+ /// initialize the SPI subssytem
void init() { /* TODO */ }
- // latch the CS select
+ /// latch the CS select
void select() { /* TODO */ }
- // release the CS select
+ /// release the CS select
void release() { /* TODO */ }
- // wait until all queued up data has been written
+ /// wait until all queued up data has been written
void waitFully();
-
- // write a byte out via SPI (returns immediately on writing register)
+
+ /// not the most efficient mechanism in the world - but should be enough for sm16716 and friends
+ template <uint8_t BIT> inline static void writeBit(uint8_t b) { /* TODO */ }
+
+ /// write a byte out via SPI (returns immediately on writing register)
void writeByte(uint8_t b) { /* TODO */ }
- // write a word out via SPI (returns immediately on writing register)
+ /// write a word out via SPI (returns immediately on writing register)
void writeWord(uint16_t w) { /* TODO */ }
- // A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
- static void writeBytesValueRaw(uint8_t value, int len) { /* TODO */ }
+ /// A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
+ static void writeBytesValueRaw(uint8_t value, int len) { /* TODO */ }
- // A full cycle of writing a value for len bytes, including select, release, and waiting
+ /// A full cycle of writing a value for len bytes, including select, release, and waiting
void writeBytesValue(uint8_t value, int len) { /* TODO */ }
- // A full cycle of writing a raw block of data out, including select, release, and waiting
+ /// A full cycle of writing a raw block of data out, including select, release, and waiting
void writeBytes(uint8_t *data, int len) { /* TODO */ }
- // write a single bit out, which bit from the passed in byte is determined by template parameter
+ /// write a single bit out, which bit from the passed in byte is determined by template parameter
template <uint8_t BIT> inline static void writeBit(uint8_t b) { /* TODO */ }
- template <uint8_t SKIP, class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) { /* TODO*/ }
-
- // template instantiations for writeBytes 3
- template <uint8_t SKIP, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
- writeBytes3<SKIP, DATA_NOP, RGB_ORDER>(data, len, scale);
- }
- template <class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
- writeBytes3<0, D, RGB_ORDER>(data, len, scale);
- }
- template <EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
- writeBytes3<0, DATA_NOP, RGB_ORDER>(data, len, scale);
- }
- void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
- writeBytes3<0, DATA_NOP, RGB>(data, len, scale);
+ /// write out pixel data from the given PixelController object
+ template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) { /* TODO */ }
};
-#endif \ No newline at end of file
+FASTLED_NAMESPACE_END
+
+#endif
diff --git a/fastspi_ref.h b/fastspi_ref.h
index 709bec34..157ea2e3 100644
--- a/fastspi_ref.h
+++ b/fastspi_ref.h
@@ -1,6 +1,8 @@
#ifndef __INC_FASTSPI_ARM_SAM_H
#define __INC_FASTSPI_ARM_SAM_H
+FASTLED_NAMESPACE_BEGIN
+
// A skeletal implementation of hardware SPI support. Fill in the necessary code for init, waiting, and writing. The rest of
// the method implementations should provide a starting point, even if not hte most efficient to start with
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
@@ -17,10 +19,10 @@ public:
void init() { /* TODO */ }
// latch the CS select
- void inline select() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->select(); } }
+ void inline select() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->select(); } }
- // release the CS select
- void inline release() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->release(); } }
+ // release the CS select
+ void inline release() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->release(); } }
// wait until all queued up data has been written
static void waitFully() { /* TODO */ }
@@ -34,7 +36,7 @@ public:
// A raw set of writing byte values, assumes setup/init/waiting done elsewhere
static void writeBytesValueRaw(uint8_t value, int len) {
while(len--) { writeByte(value); }
- }
+ }
// A full cycle of writing a value for len bytes, including select, release, and waiting
void writeBytesValue(uint8_t value, int len) {
@@ -42,16 +44,16 @@ public:
}
// A full cycle of writing a value for len bytes, including select, release, and waiting
- template <class D> void writeBytes(register uint8_t *data, int len) {
+ template <class D> void writeBytes(register uint8_t *data, int len) {
uint8_t *end = data + len;
select();
// could be optimized to write 16bit words out instead of 8bit bytes
- while(data != end) {
+ while(data != end) {
writeByte(D::adjust(*data++));
}
D::postBlock(len);
waitFully();
- release();
+ release();
}
// A full cycle of writing a value for len bytes, including select, release, and waiting
@@ -62,10 +64,10 @@ public:
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
- template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register CRGB scale, bool advance=true, uint8_t skip=0) {
+ template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
select();
- while(data != end) {
- if(FLAGS & FLAG_START_BIT) {
+ while(data != end) {
+ if(FLAGS & FLAG_START_BIT) {
writeBit<0>(1);
}
writeByte(D::adjust(pixels.loadAndScale0()));
@@ -80,19 +82,8 @@ public:
release();
}
- // template instantiations for writeBytes 3
- template <uint8_t FLAGS, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register CRGB scale, bool advance=true, uint8_t skip=0) {
- writeBytes3<FLAGS, DATA_NOP, RGB_ORDER>(data, len, scale, advance, skip);
- }
- template <class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register CRGB scale, bool advance=true, uint8_t skip=0) {
- writeBytes3<0, D, RGB_ORDER>(data, len, scale, advance, skip);
- }
- template <EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register CRGB scale, bool advance=true, uint8_t skip=0) {
- writeBytes3<0, DATA_NOP, RGB_ORDER>(data, len, scale, advance, skip);
- }
- void writeBytes3(register uint8_t *data, int len, register CRGB scale, bool advance=true, uint8_t skip=0) {
- writeBytes3<0, DATA_NOP, RGB>(data, len, scale, advance, skip);
-
};
-#endif \ No newline at end of file
+FASTLED_NAMESPACE_END
+
+#endif
diff --git a/fastspi_types.h b/fastspi_types.h
new file mode 100644
index 00000000..6fbb7e3e
--- /dev/null
+++ b/fastspi_types.h
@@ -0,0 +1,41 @@
+#ifndef __INC_FASTSPI_TYPES_H
+#define __INC_FASTSPI_TYPES_H
+
+FASTLED_NAMESPACE_BEGIN
+
+// Some helper macros for getting at mis-ordered byte values
+#define SPI_B0 (RGB_BYTE0(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
+#define SPI_B1 (RGB_BYTE1(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
+#define SPI_B2 (RGB_BYTE2(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
+#define SPI_ADVANCE (3 + (MASK_SKIP_BITS & SKIP))
+
+/// Some of the SPI controllers will need to perform a transform on each byte before doing
+/// anyting with it. Creating a class of this form and passing it in as a template parameter to
+/// writeBytes/writeBytes3 below will ensure that the body of this method will get called on every
+/// byte worked on. Recommendation, make the adjust method aggressively inlined.
+///
+/// TODO: Convinience macro for building these
+class DATA_NOP {
+public:
+ static __attribute__((always_inline)) inline uint8_t adjust(register uint8_t data) { return data; }
+ static __attribute__((always_inline)) inline uint8_t adjust(register uint8_t data, register uint8_t scale) { return scale8(data, scale); }
+ static __attribute__((always_inline)) inline void postBlock(int len) {}
+};
+
+#define FLAG_START_BIT 0x80
+#define MASK_SKIP_BITS 0x3F
+
+// Clock speed dividers
+#define SPEED_DIV_2 2
+#define SPEED_DIV_4 4
+#define SPEED_DIV_8 8
+#define SPEED_DIV_16 16
+#define SPEED_DIV_32 32
+#define SPEED_DIV_64 64
+#define SPEED_DIV_128 128
+
+#define MAX_DATA_RATE 0
+
+FASTLED_NAMESPACE_END
+
+#endif
diff --git a/hsv2rgb.cpp b/hsv2rgb.cpp
index 0844d1f2..309f9604 100644
--- a/hsv2rgb.cpp
+++ b/hsv2rgb.cpp
@@ -1,7 +1,9 @@
+#define FASTLED_INTERNAL
#include <stdint.h>
-#include "lib8tion.h"
-#include "hsv2rgb.h"
+#include "FastLED.h"
+
+FASTLED_NAMESPACE_BEGIN
// Functions to convert HSV colors to RGB colors.
//
@@ -71,29 +73,29 @@ void hsv2rgb_raw_C (const struct CHSV & hsv, struct CRGB & rgb)
// Convert hue, saturation and brightness ( HSV/HSB ) to RGB
// "Dimming" is used on saturation and brightness to make
// the output more visually linear.
-
+
// Apply dimming curves
uint8_t value = APPLY_DIMMING( hsv.val);
uint8_t saturation = hsv.sat;
-
+
// The brightness floor is minimum number that all of
// R, G, and B will be set to.
uint8_t invsat = APPLY_DIMMING( 255 - saturation);
uint8_t brightness_floor = (value * invsat) / 256;
-
+
// The color amplitude is the maximum amount of R, G, and B
// that will be added on top of the brightness_floor to
// create the specific hue desired.
uint8_t color_amplitude = value - brightness_floor;
-
+
// Figure out which section of the hue wheel we're in,
// and how far offset we are withing that section
uint8_t section = hsv.hue / HSV_SECTION_3; // 0..2
uint8_t offset = hsv.hue % HSV_SECTION_3; // 0..63
-
+
uint8_t rampup = offset; // 0..63
uint8_t rampdown = (HSV_SECTION_3 - 1) - offset; // 63..0
-
+
// We now scale rampup and rampdown to a 0-255 range -- at least
// in theory, but here's where architecture-specific decsions
// come in to play:
@@ -113,25 +115,25 @@ void hsv2rgb_raw_C (const struct CHSV & hsv, struct CRGB & rgb)
// faster to NOT multiply the ramp values by four, and just to
// divide the resulting product by 64 (instead of 256).
// Moral of the story: trust your profiler, not your insticts.
-
+
// Since there's an AVR assembly version elsewhere, we'll
// assume what we're on an architecture where any number of
// bit shifts has roughly the same cost, and we'll remove the
// redundant math at the source level:
-
+
// // scale up to 255 range
// //rampup *= 4; // 0..252
// //rampdown *= 4; // 0..252
-
+
// compute color-amplitude-scaled-down versions of rampup and rampdown
uint8_t rampup_amp_adj = (rampup * color_amplitude) / (256 / 4);
uint8_t rampdown_amp_adj = (rampdown * color_amplitude) / (256 / 4);
-
+
// add brightness_floor offset to everything
uint8_t rampup_adj_with_floor = rampup_amp_adj + brightness_floor;
uint8_t rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor;
-
-
+
+
if( section ) {
if( section == 1) {
// section 1: 0x40..0x7F
@@ -158,22 +160,22 @@ void hsv2rgb_raw_C (const struct CHSV & hsv, struct CRGB & rgb)
void hsv2rgb_raw_avr(const struct CHSV & hsv, struct CRGB & rgb)
{
uint8_t hue, saturation, value;
-
+
hue = hsv.hue;
saturation = hsv.sat;
value = hsv.val;
-
+
// Saturation more useful the other way around
saturation = 255 - saturation;
uint8_t invsat = APPLY_DIMMING( saturation );
-
+
// Apply dimming curves
value = APPLY_DIMMING( value );
-
+
// The brightness floor is minimum number that all of
// R, G, and B will be set to, which is value * invsat
uint8_t brightness_floor;
-
+
asm volatile(
"mul %[value], %[invsat] \n"
"mov %[brightness_floor], r1 \n"
@@ -182,22 +184,22 @@ void hsv2rgb_raw_avr(const struct CHSV & hsv, struct CRGB & rgb)
[invsat] "r" (invsat)
: "r0", "r1"
);
-
+
// The color amplitude is the maximum amount of R, G, and B
// that will be added on top of the brightness_floor to
// create the specific hue desired.
uint8_t color_amplitude = value - brightness_floor;
-
+
// Figure how far we are offset into the section of the
// color wheel that we're in
uint8_t offset = hsv.hue & (HSV_SECTION_3 - 1); // 0..63
uint8_t rampup = offset * 4; // 0..252
-
-
+
+
// compute color-amplitude-scaled-down versions of rampup and rampdown
uint8_t rampup_amp_adj;
uint8_t rampdown_amp_adj;
-
+
asm volatile(
"mul %[rampup], %[color_amplitude] \n"
"mov %[rampup_amp_adj], r1 \n"
@@ -210,13 +212,13 @@ void hsv2rgb_raw_avr(const struct CHSV & hsv, struct CRGB & rgb)
: [color_amplitude] "r" (color_amplitude)
: "r0", "r1"
);
-
-
+
+
// add brightness_floor offset to everything
uint8_t rampup_adj_with_floor = rampup_amp_adj + brightness_floor;
uint8_t rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor;
-
-
+
+
// keep gcc from using "X" as the index register for storing
// results back in the return structure. AVR's X register can't
// do "std X+q, rnn", but the Y and Z registers can.
@@ -224,8 +226,8 @@ void hsv2rgb_raw_avr(const struct CHSV & hsv, struct CRGB & rgb)
// extra instructions. Simply killing X here seems to help it
// try Y or Z first.
asm volatile( "" : : : "r26", "r27" );
-
-
+
+
if( hue & 0x80 ) {
// section 2: 0x80..0xBF
rgb.r = rampup_adj_with_floor;
@@ -244,7 +246,7 @@ void hsv2rgb_raw_avr(const struct CHSV & hsv, struct CRGB & rgb)
rgb.b = brightness_floor;
}
}
-
+
cleanup_R1();
}
// End of AVR asm implementation
@@ -288,32 +290,40 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb)
// G2: Whether to divide all greens by two.
// Depends GREATLY on your particular LEDs
const uint8_t G2 = 0;
-
+
// Gscale: what to scale green down by.
// Depends GREATLY on your particular LEDs
const uint8_t Gscale = 0;
-
+
uint8_t hue = hsv.hue;
uint8_t sat = hsv.sat;
uint8_t val = hsv.val;
-
+
uint8_t offset = hue & 0x1F; // 0..31
-
+
// offset8 = offset * 8
uint8_t offset8 = offset;
{
+#if defined(__AVR__)
+ // Left to its own devices, gcc turns "x <<= 3" into a loop
+ // It's much faster and smaller to just do three single-bit shifts
+ // So this business is to force that.
offset8 <<= 1;
asm volatile("");
offset8 <<= 1;
asm volatile("");
offset8 <<= 1;
+#else
+ // On ARM and other non-AVR platforms, we just shift 3.
+ offset8 <<= 3;
+#endif
}
-
+
uint8_t third = scale8( offset8, (256 / 3));
-
+
uint8_t r, g, b;
-
+
if( ! (hue & 0x80) ) {
// 0XX
if( ! (hue & 0x40) ) {
@@ -417,12 +427,12 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb)
}
}
}
-
+
// This is one of the good places to scale the green down,
// although the client can scale green down as well.
if( G2 ) g = g >> 1;
if( Gscale ) g = scale8_video_LEAVING_R1_DIRTY( g, Gscale);
-
+
// Scale down colors if we're desaturated at all
// and add the brightness_floor to r, g, and b.
if( sat != 255 ) {
@@ -431,7 +441,7 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb)
uint8_t desat = 255 - sat;
desat = scale8( desat, desat);
-
+
uint8_t brightness_floor = desat;
r += brightness_floor;
g += brightness_floor;
@@ -440,11 +450,11 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb)
// Now scale everything down if we're at value < 255.
if( val != 255 ) {
-
+
val = scale8_video_LEAVING_R1_DIRTY( val, val);
nscale8x3_video( r, g, b, val);
}
-
+
// Here we have the old AVR "missing std X+n" problem again
// It turns out that fixing it winds up costing more than
// not fixing it.
@@ -474,3 +484,146 @@ void hsv2rgb_spectrum( const struct CHSV* phsv, struct CRGB * prgb, int numLeds)
hsv2rgb_spectrum(phsv[i], prgb[i]);
}
}
+
+
+
+#define FIXFRAC8(N,D) (((N)*256)/(D))
+
+// This function is only an approximation, and it is not
+// nearly as fast as the normal HSV-to-RGB conversion.
+// See extended notes in the .h file.
+CHSV rgb2hsv_approximate( const CRGB& rgb)
+{
+ uint8_t r = rgb.r;
+ uint8_t g = rgb.g;
+ uint8_t b = rgb.b;
+ uint8_t h, s, v;
+
+ // find desaturation
+ uint8_t desat = 255;
+ if( r < desat) desat = r;
+ if( g < desat) desat = g;
+ if( b < desat) desat = b;
+
+ // remove saturation from all channels
+ r -= desat;
+ g -= desat;
+ b -= desat;
+
+ // at least one channel is now zero
+
+ // if all three channels are zero, we had a
+ // shade of gray.
+
+ uint16_t total = r + g + b;
+
+ if( total == 0) {
+ // we pick hue zero for no special reason
+ return CHSV( 0, 0, desat);
+ }
+
+ // since this wasn't a pure shade of gray,
+ // the interesting question is what hue is it
+
+ // scale all channels up to a total of 255
+ if( total != 255) {
+ uint32_t scaleup = 65535 / (total);
+ r = ((uint32_t)(r) * scaleup) / 256;
+ g = ((uint32_t)(g) * scaleup) / 256;
+ b = ((uint32_t)(b) * scaleup) / 256;
+ }
+
+ if( total > 255 ) {
+ v = 255;
+ } else {
+ v = qadd8(desat,total);
+ // undo 'dimming' of brightness
+ if( v != 255) v = sqrt16( v * 256);
+ // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5
+ // if( v != 255) v = (256.0 * sqrt( (float)(v) / 256.0));
+
+ }
+
+ // saturation is opposite of desaturation
+ s = 255 - desat;
+ if( v != 255) {
+ // this part could probably use refinement/rethinking,
+ // (but it doesn't overflow & wrap anymore)
+ uint16_t s16;
+ s16 = (s * 256) / v;
+ if( s16 < 256) {
+ s = s16;
+ } else {
+ s = 255; // clamp to prevent overflow
+ }
+ }
+
+ // undo 'dimming' of saturation
+ if( s != 255 ) s = 255 - sqrt16( (255-s) * 256);
+ // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5
+ // if( s != 255 ) s = (255 - (256.0 * sqrt( (float)(255-s) / 256.0)));
+
+ // start with which channel is highest
+ // (ties don't matter)
+ uint8_t highest = r;
+ if( g > highest) highest = g;
+ if( b > highest) highest = b;
+
+ if( highest == r ) {
+ // Red is highest.
+ // Hue could be Purple/Pink-Red,Red-Orange,Orange-Yellow
+ if( g == 0 ) {
+ // if green is zero, we're in Purple/Pink-Red
+ h = (HUE_PURPLE + HUE_PINK) / 2;
+ h += scale8( qsub8(r, 128), FIXFRAC8(48,128));
+ } else if ( (r - g) > g) {
+ // if R-G > G then we're in Red-Orange
+ h = HUE_RED;
+ h += scale8( g, FIXFRAC8(32,85));
+ } else {
+ // R-G < G, we're in Orange-Yellow
+ h = HUE_ORANGE;
+ h += scale8( qsub8((g - 85) + (171 - r), 4), FIXFRAC8(32,85)); //221
+ }
+
+ } else if ( highest == g) {
+ // Green is highest
+ // Hue could be Yellow-Green, Green-Aqua
+ if( b == 0) {
+ // if Blue is zero, we're in Yellow-Green
+ h = HUE_YELLOW;
+ h += scale8( qadd8( qadd8((g - 128), (128 - r)), 4), FIXFRAC8(32,255)); //
+ } else {
+ // if Blue is nonzero we're in Green-Aqua
+ if( (g-b) > b) {
+ h = HUE_GREEN;
+ h += scale8( b, FIXFRAC8(32,85));
+ } else {
+ h = HUE_AQUA;
+ h += scale8( qsub8(b, 85), FIXFRAC8(8,42));
+ }
+ }
+
+ } else /* highest == b */ {
+ // Blue is highest
+ // Hue could be Aqua/Blue-Blue, Blue-Purple, Purple-Pink
+ if( r == 0) {
+ // if red is zero, we're in Aqua/Blue-Blue
+ h = HUE_AQUA + ((HUE_BLUE - HUE_AQUA) / 4);
+ h += scale8( qsub8(b, 128), FIXFRAC8(24,128));
+ } else if ( (b-r) > r) {
+ // B-R > R, we're in Blue-Purple
+ h = HUE_BLUE;
+ h += scale8( r, FIXFRAC8(32,85));
+ } else {
+ // B-R < R, we're in Purple-Pink
+ h = HUE_PURPLE;
+ h += scale8( qsub8(r, 85), FIXFRAC8(32,85));
+ }
+ }
+
+ h += 1;
+ return CHSV( h, s, v);
+}
+
+FASTLED_NAMESPACE_END
diff --git a/hsv2rgb.h b/hsv2rgb.h
index 959c754a..6611298b 100644
--- a/hsv2rgb.h
+++ b/hsv2rgb.h
@@ -3,6 +3,7 @@
#include "pixeltypes.h"
+FASTLED_NAMESPACE_BEGIN
// hsv2rgb_rainbow - convert a hue, saturation, and value to RGB
// using a visually balanced rainbow (vs a straight
@@ -45,4 +46,44 @@ void hsv2rgb_raw(const struct CHSV& hsv, struct CRGB & rgb);
void hsv2rgb_raw(const struct CHSV* phsv, struct CRGB * prgb, int numLeds);
#define HUE_MAX 191
+
+// rgb2hsv_approximate - recover _approximate_ HSV values from RGB.
+//
+// NOTE 1: This function is a long-term work in process; expect
+// results to change slightly over time as this function is
+// refined and improved.
+//
+// NOTE 2: This function is most accurate when the input is an
+// RGB color that came from a fully-saturated HSV color to start
+// with. E.g. CHSV( hue, 255, 255) -> CRGB -> CHSV will give
+// best results.
+//
+// NOTE 3: This function is not nearly as fast as HSV-to-RGB.
+// It is provided for those situations when the need for this
+// function cannot be avoided, or when extremely high performance
+// is not needed.
+//
+// NOTE 4: Why is this 'only' an "approximation"?
+// Not all RGB colors have HSV equivalents! For example, there
+// is no HSV value that will ever convert to RGB(255,255,0) using
+// the code provided in this library. So if you try to
+// convert RGB(255,255,0) 'back' to HSV, you'll necessarily get
+// only an approximation. Emphasis has been placed on getting
+// the 'hue' as close as usefully possible, but even that's a bit
+// of a challenge. The 8-bit HSV and 8-bit RGB color spaces
+// are not a "bijection".
+//
+// Nevertheless, this function does a pretty good job, particularly
+// at recovering the 'hue' from fully saturated RGB colors that
+// originally came from HSV rainbow colors. So if you start
+// with CHSV(hue_in,255,255), and convert that to RGB, and then
+// convert it back to HSV using this function, the resulting output
+// hue will either exactly the same, or very close (+/-1).
+// The more desaturated the original RGB color is, the rougher the
+// approximation, and the less accurate the results.
+//
+CHSV rgb2hsv_approximate( const CRGB& rgb);
+
+FASTLED_NAMESPACE_END
+
#endif
diff --git a/keywords.txt b/keywords.txt
index e1aaebec..9cfe1d69 100644
--- a/keywords.txt
+++ b/keywords.txt
@@ -30,16 +30,16 @@ CRGBPalette256 KEYWORD1
# FastLED methods
addLeds KEYWORD2
-setBrightness KEYWORD2
-getBrightness KEYWORD2
-show KEYWORD2
-clear KEYWORD2
-showColor KEYWORD2
-setTemperature KEYWORD2
-setCorrection KEYWORD2
-setDither KEYWORD2
-countFPS KEYWORD2
-getFPS KEYWORD2
+setBrightness KEYWORD2
+getBrightness KEYWORD2
+show KEYWORD2
+clear KEYWORD2
+showColor KEYWORD2
+setTemperature KEYWORD2
+setCorrection KEYWORD2
+setDither KEYWORD2
+countFPS KEYWORD2
+getFPS KEYWORD2
# Noise methods
inoise16_raw KEYWORD2
@@ -289,28 +289,28 @@ CRGB::YellowGreen KEYWORD2
# Chipsets
LPD8806 LITERAL1
-WS2801 LITERAL1
-WS2803 LITERAL1
-P9813 LITERAL1
-SM16716 LITERAL1
-APA102 LITERAL1
-DMXSERIAL LITERAL1
-DMXSIMPLE LITERAL1
-TM1829 LITERAL1
-TM1809 LITERAL1
-TM1804 LITERAL1
+WS2801 LITERAL1
+WS2803 LITERAL1
+P9813 LITERAL1
+SM16716 LITERAL1
+APA102 LITERAL1
+DMXSERIAL LITERAL1
+DMXSIMPLE LITERAL1
+TM1829 LITERAL1
+TM1809 LITERAL1
+TM1804 LITERAL1
TM1803 LITERAL1
-APA104 LITERAL1
-WS2811 LITERAL1
+APA104 LITERAL1
+WS2811 LITERAL1
WS2812 LITERAL1
WS2812B LITERAL1
WS2811_400 LITERAL1
NEOPIXEL LITERAL1
UCS1903 LITERAL1
-UCS1903B LITERAL1
-GW6205 LITERAL1
-GW6205B LITERAL1
-LPD1886 LITERAL1
+UCS1903B LITERAL1
+GW6205 LITERAL1
+GW6205B LITERAL1
+LPD1886 LITERAL1
# RGB orderings
RGB LITERAL1
@@ -362,6 +362,6 @@ FORWARD_HUES LITERAL1
BACKWARD_HUES LITERAL1
SHORTEST_HUES LITERAL1
LONGEST_HUES LITERAL1
-BLEND LITERAL1
+LINEARBLEND LITERAL1
NOBLEND LITERAL1
diff --git a/led_sysdefs.h b/led_sysdefs.h
index 710ea552..bb5fd8ff 100644
--- a/led_sysdefs.h
+++ b/led_sysdefs.h
@@ -1,58 +1,41 @@
#ifndef __INC_LED_SYSDEFS_H
#define __INC_LED_SYSDEFS_H
-#if defined(__MK20DX128__) || defined(__MK20DX256__)
-#define FASTLED_TEENSY3
-#define FASTLED_ARM
-#if (F_CPU == 96000000)
-#define CLK_DBL 1
-#endif
+#include "fastled_config.h"
+
+#if defined(NRF51) || defined(__RFduino__)
+#include "platforms/arm/nrf51/led_sysdefs_arm_nrf51.h"
+#elif defined(__MK20DX128__) || defined(__MK20DX256__)
+// Include k20/T3 headers
+#include "platforms/arm/k20/led_sysdefs_arm_k20.h"
+#elif defined(__MKL26Z64__)
+// Include kl26/T-LC headers
+#include "platforms/arm/kl26/led_sysdefs_arm_kl26.h"
#elif defined(__SAM3X8E__)
-#define FASTLED_ARM
-#else
-#define FASTLED_AVR
-#endif
-
-#ifndef CLK_DBL
-#define CLK_DBL 0
-#endif
-
-#if defined(FASTLED_AVR) || defined(FASTLED_TEENSY3)
-#include <avr/io.h>
-#include <avr/interrupt.h> // for cli/se definitions
-
-// Define the rgister types
-#if defined(ARDUINO) // && ARDUINO < 150
-typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
-typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
-#endif
-
+// Include sam/due headers
+#include "platforms/arm/sam/led_sysdefs_arm_sam.h"
+#elif defined(STM32F10X_MD)
+#include "platforms/arm/stm32/led_sysdefs_arm_stm32.h"
+#elif defined(__SAMD21G18A__)
+#include "platforms/arm/d21/led_sysdefs_arm_d21.h"
+#elif defined(__XTENSA__)
+#error "XTENSA-architecture microcontrollers are not supported."
#else
-// reuseing/abusing cli/sei defs for due
-#define cli() __disable_irq(); __disable_fault_irq();
-#define sei() __enable_irq(); __enable_fault_irq();
-
-#endif
-
-#if 0
-#if defined(ARDUINO) && defined(FASTLED_AVR) && ARDUINO >= 157
-#error Arduion versions 1.5.7 and later not yet supported by FastLED for AVR
+// AVR platforms
+#include "platforms/avr/led_sysdefs_avr.h"
#endif
-#if defined(ARDUINO) && defined (FASTLED_AVR) && (__GNUC__ == 4) && (__GNUC_MINOR__ > 7)
-#error gcc versions 4.8 and above are not yet supported by FastLED for AVR
-#endif
+#ifndef FASTLED_NAMESPACE_BEGIN
+#define FASTLED_NAMESPACE_BEGIN
+#define FASTLED_NAMESPACE_END
+#define FASTLED_USING_NAMESPACE
#endif
-// Arduino.h needed for convinience functions digitalPinToPort/BitMask/portOutputRegister and the pinMode methods.
+// Arduino.h needed for convenience functions digitalPinToPort/BitMask/portOutputRegister and the pinMode methods.
+#ifdef ARDUINO
#include<Arduino.h>
-
-// Scaling macro choice
-#if defined(LIB8_ATTINY)
-# define INLINE_SCALE(B, SCALE) delaycycles<3>()
-# warning "No hardware multiply, inline brightness scaling disabled"
-#else
-# define INLINE_SCALE(B, SCALE) B = scale8_video(B, SCALE)
#endif
+#define CLKS_PER_US (F_CPU/1000000)
+
#endif
diff --git a/lib8tion.cpp b/lib8tion.cpp
index d7d6b13e..1306e5c0 100644
--- a/lib8tion.cpp
+++ b/lib8tion.cpp
@@ -1,4 +1,8 @@
+#define FASTLED_INTERNAL
#include <stdint.h>
+#include "FastLED.h"
+
+FASTLED_NAMESPACE_BEGIN
#define RAND16_SEED 1337
uint16_t rand16seed = RAND16_SEED;
@@ -52,7 +56,7 @@ void * memset8 ( void * ptr, uint8_t val, uint16_t num )
//__attribute__ ((noinline))
-void * memcpy8 ( void * dst, void* src, uint16_t num )
+void * memcpy8 ( void * dst, const void* src, uint16_t num )
{
asm volatile(
" movw r30, %[src] \n\t"
@@ -80,7 +84,7 @@ void * memcpy8 ( void * dst, void* src, uint16_t num )
}
//__attribute__ ((noinline))
-void * memmove8 ( void * dst, void* src, uint16_t num )
+void * memmove8 ( void * dst, const void* src, uint16_t num )
{
if( src > dst) {
// if src > dst then we can use the forward-stepping memcpy8
@@ -120,6 +124,9 @@ void * memmove8 ( void * dst, void* src, uint16_t num )
#endif /* AVR */
+
+
+
#if 0
// TEST / VERIFICATION CODE ONLY BELOW THIS POINT
#include <Arduino.h>
@@ -146,7 +153,7 @@ void testmul8()
{
delay(5000);
byte r, c;
-
+
Serial.println("mul8:");
for( r = 0; r <= 20; r += 1) {
Serial.print(r); Serial.print(" : ");
@@ -220,19 +227,19 @@ void testnscale8x3()
byte r, g, b, sc;
for( byte z = 0; z < 10; z++) {
r = random8(); g = random8(); b = random8(); sc = random8();
-
+
Serial.print("nscale8x3_video( ");
Serial.print(r); Serial.print(", ");
Serial.print(g); Serial.print(", ");
Serial.print(b); Serial.print(", ");
Serial.print(sc); Serial.print(") = [ ");
-
+
nscale8x3_video( r, g, b, sc);
-
+
Serial.print(r); Serial.print(", ");
Serial.print(g); Serial.print(", ");
Serial.print(b); Serial.print("]");
-
+
Serial.println(' ');
}
Serial.println("done.");
@@ -240,3 +247,5 @@ void testnscale8x3()
}
#endif
+
+FASTLED_NAMESPACE_END
diff --git a/lib8tion.h b/lib8tion.h
index 228fe8f9..b69a3f8c 100644
--- a/lib8tion.h
+++ b/lib8tion.h
@@ -1,6 +1,12 @@
+#ifndef __INC_LED_SYSDEFS_H
+#error WTH? led_sysdefs needs to be included first
+#endif
+
#ifndef __INC_LIB8TION_H
#define __INC_LIB8TION_H
+FASTLED_NAMESPACE_BEGIN
+
/*
Fast, efficient 8-bit math functions specifically
@@ -109,7 +115,7 @@
faster and five times smaller than Arduino's built-in
generic 32-bit sqrt routine.
sqrt16( uint16_t x ) == sqrt( x)
-
+
- Dimming and brightening functions for 8-bit
light values.
dim8_video( x) == scale8_video( x, x)
@@ -145,13 +151,19 @@
Sine wave beat generators can specify a low and
high range for the output. Sawtooth wave beat
generators always range 0-255 or 0-65535.
- beatsin8( BPM, low8, high8)
+ beatsin8( BPM, low8, high8)
= (sine(beatphase) * (high8-low8)) + low8
beatsin16( BPM, low16, high16)
= (sine(beatphase) * (high16-low16)) + low16
+ beatsin88( BPM88, low16, high16)
+ = (sine(beatphase) * (high16-low16)) + low16
beat8( BPM) = 8-bit repeating sawtooth wave
beat16( BPM) = 16-bit repeating sawtooth wave
-
+ beat88( BPM88) = 16-bit repeating sawtooth wave
+ BPM is beats per minute in either simple form
+ e.g. 120, or Q8.8 fixed-point form.
+ BPM88 is beats per minute in ONLY Q8.8 fixed-point
+ form.
Lib8tion is pronounced like 'libation': lie-BAY-shun
@@ -168,7 +180,7 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun
// for memmove, memcpy, and memset if not defined here
#endif
-#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
+#if defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega8U2__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny167__) || defined(__AVR_ATtiny87__)
#define LIB8_ATTINY 1
#endif
@@ -197,6 +209,8 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun
#define ADD8_C 1
#define SUB8_C 1
#define EASE8_C 1
+#define AVG8_C 1
+#define AVG7_C 1
#elif defined(__AVR__)
@@ -209,6 +223,8 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun
#define ABS8_C 0
#define ADD8_C 0
#define SUB8_C 0
+#define AVG8_C 0
+#define AVG7_C 0
#define QADD8_AVRASM 1
#define QADD7_AVRASM 1
@@ -216,6 +232,8 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun
#define ABS8_AVRASM 1
#define ADD8_AVRASM 1
#define SUB8_AVRASM 1
+#define AVG8_AVRASM 1
+#define AVG7_AVRASM 1
// Note: these require hardware MUL instruction
// -- sorry, ATtiny!
@@ -265,9 +283,15 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun
#define ADD8_C 1
#define SUB8_C 1
#define EASE8_C 1
+#define AVG8_C 1
+#define AVG7_C 1
#endif
+///@defgroup lib8tion Fast math functions
+///A variety of functions for working with numbers.
+///@{
+
///////////////////////////////////////////////////////////////////////
//
@@ -295,26 +319,36 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun
// in steps of 0.00003051757
//
-typedef uint8_t fract8; // ANSI: unsigned short _Fract
-typedef int8_t sfract7; // ANSI: signed short _Fract
-typedef uint16_t fract16; // ANSI: unsigned _Fract
-typedef int16_t sfract15; // ANSI: signed _Fract
+/// ANSI unsigned short _Fract. range is 0 to 0.99609375
+/// in steps of 0.00390625
+typedef uint8_t fract8; ///< ANSI: unsigned short _Fract
+
+/// ANSI: signed short _Fract. range is -0.9921875 to 0.9921875
+/// in steps of 0.0078125
+typedef int8_t sfract7; ///< ANSI: signed short _Fract
+
+/// ANSI: unsigned _Fract. range is 0 to 0.99998474121
+/// in steps of 0.00001525878
+typedef uint16_t fract16; ///< ANSI: unsigned _Fract
+
+/// ANSI: signed _Fract. range is -0.99996948242 to 0.99996948242
+/// in steps of 0.00003051757
+typedef int16_t sfract15; ///< ANSI: signed _Fract
// accumXY types should be interpreted as X bits of integer,
// and Y bits of fraction.
// E.g., accum88 has 8 bits of int, 8 bits of fraction
-typedef uint16_t accum88; // ANSI: unsigned short _Accum
-typedef int16_t saccum78; // ANSI: signed short _Accum
-typedef uint32_t accum1616;// ANSI: signed _Accum
-typedef int32_t saccum1516;//ANSI: signed _Accum
-typedef uint16_t accum124; // no direct ANSI counterpart
-typedef int32_t saccum114;// no direct ANSI counterpart
-
+typedef uint16_t accum88; ///< ANSI: unsigned short _Accum. 8 bits int, 8 bits fraction
+typedef int16_t saccum78; ///< ANSI: signed short _Accum. 7 bits int, 8 bits fraction
+typedef uint32_t accum1616;///< ANSI: signed _Accum. 16 bits int, 16 bits fraction
+typedef int32_t saccum1516;///< ANSI: signed _Accum. 15 bits int, 16 bits fraction
+typedef uint16_t accum124; ///< no direct ANSI counterpart. 12 bits int, 4 bits fraction
+typedef int32_t saccum114;///< no direct ANSI counterpart. 1 bit int, 14 bits fraction
-// typedef for IEEE754 "binary32" float type internals
+/// typedef for IEEE754 "binary32" float type internals
typedef union {
uint32_t i;
float f;
@@ -336,630 +370,17 @@ typedef union {
};
} IEEE754binary32_t;
-
+#include "lib8tion/math8.h"
+#include "lib8tion/scale8.h"
+#include "lib8tion/random8.h"
+#include "lib8tion/trig8.h"
///////////////////////////////////////////////////////////////////////
-// qadd8: add one byte to another, saturating at 0xFF
-LIB8STATIC uint8_t qadd8( uint8_t i, uint8_t j)
-{
-#if QADD8_C == 1
- int t = i + j;
- if( t > 255) t = 255;
- return t;
-#elif QADD8_AVRASM == 1
- asm volatile(
- /* First, add j to i, conditioning the C flag */
- "add %0, %1 \n\t"
-
- /* Now test the C flag.
- If C is clear, we branch around a load of 0xFF into i.
- If C is set, we go ahead and load 0xFF into i.
- */
- "brcc L_%= \n\t"
- "ldi %0, 0xFF \n\t"
- "L_%=: "
- : "+a" (i)
- : "a" (j) );
- return i;
-#elif QADD8_ARM_DSP_ASM == 1
- asm volatile( "uqadd8 %0, %0, %1" : "+r" (i) : "r" (j));
- return i;
-#else
-#error "No implementation for qadd8 available."
-#endif
-}
-
-
-// qadd7: add one signed byte to another,
-// saturating at 0x7F.
-LIB8STATIC int8_t qadd7( int8_t i, int8_t j)
-{
-#if QADD7_C == 1
- int16_t t = i + j;
- if( t > 127) t = 127;
- return t;
-#elif QADD7_AVRASM == 1
- asm volatile(
- /* First, add j to i, conditioning the V flag */
- "add %0, %1 \n\t"
-
- /* Now test the V flag.
- If V is clear, we branch around a load of 0x7F into i.
- If V is set, we go ahead and load 0x7F into i.
- */
- "brvc L_%= \n\t"
- "ldi %0, 0x7F \n\t"
- "L_%=: "
- : "+a" (i)
- : "a" (j) );
-
- return i;
-#elif QADD7_ARM_DSP_ASM == 1
- asm volatile( "qadd8 %0, %0, %1" : "+r" (i) : "r" (j));
- return i;
-#else
-#error "No implementation for qadd7 available."
-#endif
-}
-
-// qsub8: subtract one byte from another, saturating at 0x00
-LIB8STATIC uint8_t qsub8( uint8_t i, uint8_t j)
-{
-#if QSUB8_C == 1
- int t = i - j;
- if( t < 0) t = 0;
- return t;
-#elif QSUB8_AVRASM == 1
-
- asm volatile(
- /* First, subtract j from i, conditioning the C flag */
- "sub %0, %1 \n\t"
-
- /* Now test the C flag.
- If C is clear, we branch around a load of 0x00 into i.
- If C is set, we go ahead and load 0x00 into i.
- */
- "brcc L_%= \n\t"
- "ldi %0, 0x00 \n\t"
- "L_%=: "
- : "+a" (i)
- : "a" (j) );
-
- return i;
-#else
-#error "No implementation for qsub8 available."
-#endif
-}
-
-// add8: add one byte to another, with one byte result
-LIB8STATIC uint8_t add8( uint8_t i, uint8_t j)
-{
-#if ADD8_C == 1
- int t = i + j;
- return t;
-#elif ADD8_AVRASM == 1
- // Add j to i, period.
- asm volatile( "add %0, %1" : "+a" (i) : "a" (j));
- return i;
-#else
-#error "No implementation for add8 available."
-#endif
-}
-
-
-// sub8: subtract one byte from another, 8-bit result
-LIB8STATIC uint8_t sub8( uint8_t i, uint8_t j)
-{
-#if SUB8_C == 1
- int t = i - j;
- return t;
-#elif SUB8_AVRASM == 1
- // Subtract j from i, period.
- asm volatile( "sub %0, %1" : "+a" (i) : "a" (j));
- return i;
-#else
-#error "No implementation for sub8 available."
-#endif
-}
-
-
-// scale8: scale one byte by a second one, which is treated as
-// the numerator of a fraction whose denominator is 256
-// In other words, it computes i * (scale / 256)
-// 4 clocks AVR, 2 clocks ARM
-LIB8STATIC uint8_t scale8( uint8_t i, fract8 scale)
-{
-#if SCALE8_C == 1
- return
- ((int)i * (int)(scale) ) >> 8;
-#elif SCALE8_AVRASM == 1
-#if defined(LIB8_ATTINY)
- uint8_t work=0;
- uint8_t cnt=0x80;
- asm volatile(
- "LOOP_%=: \n\t"
- /*" sbrc %[scale], 0 \n\t"
- " add %[work], %[i] \n\t"
- " ror %[work] \n\t"
- " lsr %[scale] \n\t"
- " clc \n\t"*/
- " sbrc %[scale], 0 \n\t"
- " add %[work], %[i] \n\t"
- " ror %[work] \n\t"
- " lsr %[scale] \n\t"
- " lsr %[cnt] \n\t"
- "brcc LOOP_%="
- : [work] "+r" (work), [cnt] "+r" (cnt)
- : [scale] "r" (scale), [i] "r" (i)
- :
- );
- return work;
-#else
- asm volatile(
- /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
- "mul %0, %1 \n\t"
- /* Move the high 8-bits of the product (r1) back to i */
- "mov %0, r1 \n\t"
- /* Restore r1 to "0"; it's expected to always be that */
- "clr __zero_reg__ \n\t"
-
- : "+a" (i) /* writes to i */
- : "a" (scale) /* uses scale */
- : "r0", "r1" /* clobbers r0, r1 */ );
-
- /* Return the result */
- return i;
-#endif
-#else
-#error "No implementation for scale8 available."
-#endif
-}
-
-// The "video" version of scale8 guarantees that the output will
-// be only be zero if one or both of the inputs are zero. If both
-// inputs are non-zero, the output is guaranteed to be non-zero.
-// This makes for better 'video'/LED dimming, at the cost of
-// several additional cycles.
-LIB8STATIC uint8_t scale8_video( uint8_t i, fract8 scale)
-{
-#if SCALE8_C == 1 || defined(LIB8_ATTINY)
- uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
- // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
- // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
- return j;
-#elif SCALE8_AVRASM == 1
- uint8_t j=0;
- asm volatile(
- " tst %[i]\n\t"
- " breq L_%=\n\t"
- " mul %[i], %[scale]\n\t"
- " mov %[j], r1\n\t"
- " clr __zero_reg__\n\t"
- " cpse %[scale], r1\n\t"
- " subi %[j], 0xFF\n\t"
- "L_%=: \n\t"
- : [j] "+a" (j)
- : [i] "a" (i), [scale] "a" (scale)
- : "r0", "r1");
-
- return j;
- // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
- // asm volatile(
- // " tst %0 \n"
- // " breq L_%= \n"
- // " mul %0, %1 \n"
- // " mov %0, r1 \n"
- // " add %0, %2 \n"
- // " clr __zero_reg__ \n"
- // "L_%=: \n"
-
- // : "+a" (i)
- // : "a" (scale), "a" (nonzeroscale)
- // : "r0", "r1");
-
- // // Return the result
- // return i;
-#else
-#error "No implementation for scale8_video available."
-#endif
-}
-// This version of scale8 does not clean up the R1 register on AVR
-// If you are doing several 'scale8's in a row, use this, and
-// then explicitly call cleanup_R1.
-LIB8STATIC uint8_t scale8_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
-{
-#if SCALE8_C == 1
- return ((int)i * (int)(scale) ) >> 8;
-#elif SCALE8_AVRASM == 1
- asm volatile(
- /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
- "mul %0, %1 \n\t"
- /* Move the high 8-bits of the product (r1) back to i */
- "mov %0, r1 \n\t"
- /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */
- /* "clr __zero_reg__ \n\t" */
-
- : "+a" (i) /* writes to i */
- : "a" (scale) /* uses scale */
- : "r0", "r1" /* clobbers r0, r1 */ );
-
- // Return the result
- return i;
-#else
-#error "No implementation for scale8_LEAVING_R1_DIRTY available."
-#endif
-}
-// THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENT DIRECTLY IN PLACE
-
-LIB8STATIC void nscale8_LEAVING_R1_DIRTY( uint8_t& i, fract8 scale)
-{
-#if SCALE8_C == 1
- i = ((int)i * (int)(scale) ) >> 8;
-#elif SCALE8_AVRASM == 1
- asm volatile(
- /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
- "mul %0, %1 \n\t"
- /* Move the high 8-bits of the product (r1) back to i */
- "mov %0, r1 \n\t"
- /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */
- /* "clr __zero_reg__ \n\t" */
-
- : "+a" (i) /* writes to i */
- : "a" (scale) /* uses scale */
- : "r0", "r1" /* clobbers r0, r1 */ );
-#else
-#error "No implementation for nscale8_LEAVING_R1_DIRTY available."
-#endif
-}
-
-
-
-LIB8STATIC uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
-{
-#if SCALE8_C == 1 || defined(LIB8_ATTINY)
- uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
- // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
- // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
- return j;
-#elif SCALE8_AVRASM == 1
- uint8_t j=0;
- asm volatile(
- " tst %[i]\n\t"
- " breq L_%=\n\t"
- " mul %[i], %[scale]\n\t"
- " mov %[j], r1\n\t"
- " breq L_%=\n\t"
- " subi %[j], 0xFF\n\t"
- "L_%=: \n\t"
- : [j] "+a" (j)
- : [i] "a" (i), [scale] "a" (scale)
- : "r0", "r1");
-
- return j;
- // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
- // asm volatile(
- // " tst %0 \n"
- // " breq L_%= \n"
- // " mul %0, %1 \n"
- // " mov %0, r1 \n"
- // " add %0, %2 \n"
- // " clr __zero_reg__ \n"
- // "L_%=: \n"
-
- // : "+a" (i)
- // : "a" (scale), "a" (nonzeroscale)
- // : "r0", "r1");
-
- // // Return the result
- // return i;
-#else
-#error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
-#endif
-}
-
-LIB8STATIC void nscale8_video_LEAVING_R1_DIRTY( uint8_t & i, fract8 scale)
-{
-#if SCALE8_C == 1 || defined(LIB8_ATTINY)
- i = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
-#elif SCALE8_AVRASM == 1
- asm volatile(
- " tst %[i]\n\t"
- " breq L_%=\n\t"
- " mul %[i], %[scale]\n\t"
- " mov %[i], r1\n\t"
- " breq L_%=\n\t"
- " subi %[i], 0xFF\n\t"
- "L_%=: \n\t"
- : [i] "+a" (i)
- : [scale] "a" (scale)
- : "r0", "r1");
-#else
-#error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
-#endif
-}
-
-
-LIB8STATIC void cleanup_R1()
-{
-#if CLEANUP_R1_AVRASM == 1
- // Restore r1 to "0"; it's expected to always be that
- asm volatile( "clr __zero_reg__ \n\t" : : : "r1" );
-#endif
-}
-
-
-// nscale8x3: scale three one byte values by a fourth one, which is treated as
-// the numerator of a fraction whose demominator is 256
-// In other words, it computes r,g,b * (scale / 256)
-//
-// THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENTS IN PLACE
-
-LIB8STATIC void nscale8x3( uint8_t& r, uint8_t& g, uint8_t& b, fract8 scale)
-{
-#if SCALE8_C == 1
- r = ((int)r * (int)(scale) ) >> 8;
- g = ((int)g * (int)(scale) ) >> 8;
- b = ((int)b * (int)(scale) ) >> 8;
-#elif SCALE8_AVRASM == 1
- r = scale8_LEAVING_R1_DIRTY(r, scale);
- g = scale8_LEAVING_R1_DIRTY(g, scale);
- b = scale8_LEAVING_R1_DIRTY(b, scale);
- cleanup_R1();
-#else
-#error "No implementation for nscale8x3 available."
-#endif
-}
-
-
-LIB8STATIC void nscale8x3_video( uint8_t& r, uint8_t& g, uint8_t& b, fract8 scale)
-{
-#if SCALE8_C == 1
- uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
- r = (r == 0) ? 0 : (((int)r * (int)(scale) ) >> 8) + nonzeroscale;
- g = (g == 0) ? 0 : (((int)g * (int)(scale) ) >> 8) + nonzeroscale;
- b = (b == 0) ? 0 : (((int)b * (int)(scale) ) >> 8) + nonzeroscale;
-#elif SCALE8_AVRASM == 1
- nscale8_video_LEAVING_R1_DIRTY( r, scale);
- nscale8_video_LEAVING_R1_DIRTY( g, scale);
- nscale8_video_LEAVING_R1_DIRTY( b, scale);
- cleanup_R1();
-#else
-#error "No implementation for nscale8x3 available."
-#endif
-}
-
-// nscale8x2: scale two one byte values by a third one, which is treated as
-// the numerator of a fraction whose demominator is 256
-// In other words, it computes i,j * (scale / 256)
-//
-// THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENTS IN PLACE
-
-LIB8STATIC void nscale8x2( uint8_t& i, uint8_t& j, fract8 scale)
-{
-#if SCALE8_C == 1
- i = ((int)i * (int)(scale) ) >> 8;
- j = ((int)j * (int)(scale) ) >> 8;
-#elif SCALE8_AVRASM == 1
- i = scale8_LEAVING_R1_DIRTY(i, scale);
- j = scale8_LEAVING_R1_DIRTY(j, scale);
- cleanup_R1();
-#else
-#error "No implementation for nscale8x2 available."
-#endif
-}
-
-
-LIB8STATIC void nscale8x2_video( uint8_t& i, uint8_t& j, fract8 scale)
-{
-#if SCALE8_C == 1
- uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
- i = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
- j = (j == 0) ? 0 : (((int)j * (int)(scale) ) >> 8) + nonzeroscale;
-#elif SCALE8_AVRASM == 1
- nscale8_video_LEAVING_R1_DIRTY( i, scale);
- nscale8_video_LEAVING_R1_DIRTY( j, scale);
- cleanup_R1();
-#else
-#error "No implementation for nscale8x2 available."
-#endif
-}
-
-
-// scale16by8: scale a 16-bit unsigned value by an 8-bit value,
-// considered as numerator of a fraction whose denominator
-// is 256. In other words, it computes i * (scale / 256)
-
-#if SCALE16BY8_C == 1
-LIB8STATIC uint16_t scale16by8( uint16_t i, fract8 scale )
-{
- uint16_t result;
- result = (i * scale) / 256;
- return result;
-}
-#elif SCALE16BY8_AVRASM == 1
-LIB8STATIC uint16_t scale16by8( uint16_t i, fract8 scale )
-{
- uint16_t result;
- asm volatile(
- // result.A = HighByte(i.A x j )
- " mul %A[i], %[scale] \n\t"
- " mov %A[result], r1 \n\t"
- " clr %B[result] \n\t"
-
- // result.A-B += i.B x j
- " mul %B[i], %[scale] \n\t"
- " add %A[result], r0 \n\t"
- " adc %B[result], r1 \n\t"
-
- // cleanup r1
- " clr __zero_reg__ \n\t"
-
- : [result] "=r" (result)
- : [i] "r" (i), [scale] "r" (scale)
- : "r0", "r1"
- );
- return result;
-}
-#else
-#error "No implementation for scale16by8 available."
-#endif
-
-// scale16: scale a 16-bit unsigned value by a 16-bit value,
-// considered as numerator of a fraction whose denominator
-// is 65536. In other words, it computes i * (scale / 65536)
-
-#if SCALE16_C == 1
-LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale )
-{
- uint16_t result;
- result = ((uint32_t)(i) * (uint32_t)(scale)) / 65536;
- return result;
-}
-#elif SCALE16_AVRASM == 1
-LIB8STATIC
-uint16_t scale16( uint16_t i, fract16 scale )
-{
- uint32_t result = 0;
- const uint8_t zero = 0;
- asm volatile(
- // result.A-B = i.A x scale.A
- " mul %A[i], %A[scale] \n\t"
- // save results...
- // basic idea:
- //" mov %A[result], r0 \n\t"
- //" mov %B[result], r1 \n\t"
- // which can be written as...
- " movw %A[result], r0 \n\t"
- // We actually need to do anything with r0,
- // as result.A is never used again here, so we
- // could just move the high byte, but movw is
- // one clock cycle, just like mov, so might as
- // well, in case we want to use this code for
- // a generic 16x16 multiply somewhere.
-
- // result.C-D = i.B x scale.B
- " mul %B[i], %B[scale] \n\t"
- //" mov %C[result], r0 \n\t"
- //" mov %D[result], r1 \n\t"
- " movw %C[result], r0 \n\t"
-
- // result.B-D += i.B x scale.A
- " mul %B[i], %A[scale] \n\t"
-
- " add %B[result], r0 \n\t"
- " adc %C[result], r1 \n\t"
- " adc %D[result], %[zero] \n\t"
-
- // result.B-D += i.A x scale.B
- " mul %A[i], %B[scale] \n\t"
-
- " add %B[result], r0 \n\t"
- " adc %C[result], r1 \n\t"
- " adc %D[result], %[zero] \n\t"
-
- // cleanup r1
- " clr r1 \n\t"
-
- : [result] "+r" (result)
- : [i] "r" (i),
- [scale] "r" (scale),
- [zero] "r" (zero)
- : "r0", "r1"
- );
- result = result >> 16;
- return result;
-}
-#else
-#error "No implementation for scale16 available."
-#endif
-
-
-
-// mul8: 8x8 bit multiplication, with 8 bit result
-LIB8STATIC uint8_t mul8( uint8_t i, uint8_t j)
-{
-#if MUL8_C == 1
- return ((int)i * (int)(j) ) & 0xFF;
-#elif MUL8_AVRASM == 1
- asm volatile(
- /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
- "mul %0, %1 \n\t"
- /* Extract the LOW 8-bits (r0) */
- "mov %0, r0 \n\t"
- /* Restore r1 to "0"; it's expected to always be that */
- "clr __zero_reg__ \n\t"
- : "+a" (i)
- : "a" (j)
- : "r0", "r1");
-
- return i;
-#else
-#error "No implementation for mul8 available."
-#endif
-}
-
-
-// mul8: saturating 8x8 bit multiplication, with 8 bit result
-LIB8STATIC uint8_t qmul8( uint8_t i, uint8_t j)
-{
-#if QMUL8_C == 1
- int p = ((int)i * (int)(j) );
- if( p > 255) p = 255;
- return p;
-#elif QMUL8_AVRASM == 1
- asm volatile(
- /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
- " mul %0, %1 \n\t"
- /* If high byte of result is zero, all is well. */
- " tst r1 \n\t"
- " breq Lnospill_%= \n\t"
- /* If high byte of result > 0, saturate low byte to 0xFF */
- " ldi %0,0xFF \n\t"
- " rjmp Ldone_%= \n\t"
- "Lnospill_%=: \n\t"
- /* Extract the LOW 8-bits (r0) */
- " mov %0, r0 \n\t"
- "Ldone_%=: \n\t"
- /* Restore r1 to "0"; it's expected to always be that */
- " clr __zero_reg__ \n\t"
- : "+a" (i)
- : "a" (j)
- : "r0", "r1");
-
- return i;
-#else
-#error "No implementation for qmul8 available."
-#endif
-}
-
-
-// abs8: take abs() of a signed 8-bit uint8_t
-LIB8STATIC int8_t abs8( int8_t i)
-{
-#if ABS8_C == 1
- if( i < 0) i = -i;
- return i;
-#elif ABS8_AVRASM == 1
-
-
- asm volatile(
- /* First, check the high bit, and prepare to skip if it's clear */
- "sbrc %0, 7 \n"
-
- /* Negate the value */
- "neg %0 \n"
-
- : "+r" (i) : "r" (i) );
- return i;
-#else
-#error "No implementation for abs8 available."
-#endif
-}
///////////////////////////////////////////////////////////////////////
@@ -968,22 +389,18 @@ LIB8STATIC int8_t abs8( int8_t i)
//
// Note that anything involving a 'float' on AVR will be slower.
-// floatToSfract15: conversion from IEEE754 float in the range (-1,1)
-// to 16-bit fixed point. Note that the extremes of
-// one and negative one are NOT representable. The
-// representable range is basically
-//
-// sfract15ToFloat: conversion from sfract15 fixed point to
-// IEEE754 32-bit float.
-
-LIB8STATIC
-float sfract15ToFloat( sfract15 y)
+/// sfract15ToFloat: conversion from sfract15 fixed point to
+/// IEEE754 32-bit float.
+LIB8STATIC float sfract15ToFloat( sfract15 y)
{
return y / 32768.0;
}
-LIB8STATIC
-sfract15 floatToSfract15( float f)
+/// conversion from IEEE754 float in the range (-1,1)
+/// to 16-bit fixed point. Note that the extremes of
+/// one and negative one are NOT representable. The
+/// representable range is basically
+LIB8STATIC sfract15 floatToSfract15( float f)
{
return f * 32768.0;
}
@@ -991,352 +408,6 @@ sfract15 floatToSfract15( float f)
///////////////////////////////////////////////////////////////////////
-
-// Dimming and brightening functions
-//
-// The eye does not respond in a linear way to light.
-// High speed PWM'd LEDs at 50% duty cycle appear far
-// brighter then the 'half as bright' you might expect.
-//
-// If you want your midpoint brightness leve (128) to
-// appear half as bright as 'full' brightness (255), you
-// have to apply a 'dimming function'.
-//
-//
-
-LIB8STATIC uint8_t dim8_raw( uint8_t x)
-{
- return scale8( x, x);
-}
-
-LIB8STATIC uint8_t dim8_video( uint8_t x)
-{
- return scale8_video( x, x);
-}
-
-LIB8STATIC uint8_t dim8_lin( uint8_t x )
-{
- if( x & 0x80 ) {
- x = scale8( x, x);
- } else {
- x += 1;
- x /= 2;
- }
- return x;
-}
-
-LIB8STATIC uint8_t brighten8_raw( uint8_t x)
-{
- uint8_t ix = 255 - x;
- return 255 - scale8( ix, ix);
-}
-
-LIB8STATIC uint8_t brighten8_video( uint8_t x)
-{
- uint8_t ix = 255 - x;
- return 255 - scale8_video( ix, ix);
-}
-
-LIB8STATIC uint8_t brighten8_lin( uint8_t x )
-{
- uint8_t ix = 255 - x;
- if( ix & 0x80 ) {
- ix = scale8( ix, ix);
- } else {
- ix += 1;
- ix /= 2;
- }
- return 255 - ix;
-}
-
-///////////////////////////////////////////////////////////////////////
-
-// A 16-bit PNRG good enough for LED animations
-
-// X(n+1) = (2053 * X(n)) + 13849)
-#define RAND16_2053 2053
-#define RAND16_13849 13849
-
-extern uint16_t rand16seed;// = RAND16_SEED;
-
-
-LIB8STATIC uint8_t random8()
-{
- rand16seed = (rand16seed * RAND16_2053) + RAND16_13849;
- return rand16seed;
-}
-
-LIB8STATIC uint16_t random16()
-{
- rand16seed = (rand16seed * RAND16_2053) + RAND16_13849;
- return rand16seed;
-}
-
-
-LIB8STATIC uint8_t random8(uint8_t lim)
-{
- uint8_t r = random8();
- r = scale8( r, lim);
- return r;
-}
-
-LIB8STATIC uint8_t random8(uint8_t min, uint8_t lim)
-{
- uint8_t delta = lim - min;
- uint8_t r = random8(delta) + min;
- return r;
-}
-
-LIB8STATIC uint16_t random16( uint16_t lim)
-{
- uint16_t r = random16();
- uint32_t p = (uint32_t)lim * (uint32_t)r;
- r = p >> 16;
- return r;
-}
-
-LIB8STATIC uint16_t random16( uint16_t min, uint16_t lim)
-{
- uint16_t delta = lim - min;
- uint16_t r = random16( delta) + min;
- return r;
-}
-
-LIB8STATIC void random16_set_seed( uint16_t seed)
-{
- rand16seed = seed;
-}
-
-LIB8STATIC uint16_t random16_get_seed()
-{
- return rand16seed;
-}
-
-LIB8STATIC void random16_add_entropy( uint16_t entropy)
-{
- rand16seed += entropy;
-}
-
-
-///////////////////////////////////////////////////////////////////////
-
-// sin16 & cos16:
-// Fast 16-bit approximations of sin(x) & cos(x).
-// Input angle is an unsigned int from 0-65535.
-// Output is signed int from -32767 to 32767.
-//
-// This approximation never varies more than 0.69%
-// from the floating point value you'd get by doing
-// float s = sin( x ) * 32767.0;
-//
-// Don't use this approximation for calculating the
-// trajectory of a rocket to Mars, but it's great
-// for art projects and LED displays.
-//
-// On Arduino/AVR, this approximation is more than
-// 10X faster than floating point sin(x) and cos(x)
-
-#if defined(__AVR__)
-#define sin16 sin16_avr
-#else
-#define sin16 sin16_C
-#endif
-
-LIB8STATIC int16_t sin16_avr( uint16_t theta )
-{
- static const uint8_t data[] =
- { 0, 0, 49, 0, 6393%256, 6393/256, 48, 0,
- 12539%256, 12539/256, 44, 0, 18204%256, 18204/256, 38, 0,
- 23170%256, 23170/256, 31, 0, 27245%256, 27245/256, 23, 0,
- 30273%256, 30273/256, 14, 0, 32137%256, 32137/256, 4 /*,0*/ };
-
- uint16_t offset = (theta & 0x3FFF);
-
- // AVR doesn't have a multi-bit shift instruction,
- // so if we say "offset >>= 3", gcc makes a tiny loop.
- // Inserting empty volatile statements between each
- // bit shift forces gcc to unroll the loop.
- offset >>= 1; // 0..8191
- asm volatile("");
- offset >>= 1; // 0..4095
- asm volatile("");
- offset >>= 1; // 0..2047
-
- if( theta & 0x4000 ) offset = 2047 - offset;
-
- uint8_t sectionX4;
- sectionX4 = offset / 256;
- sectionX4 *= 4;
-
- uint8_t m;
-
- union {
- uint16_t b;
- struct {
- uint8_t blo;
- uint8_t bhi;
- };
- } u;
-
- //in effect u.b = blo + (256 * bhi);
- u.blo = data[ sectionX4 ];
- u.bhi = data[ sectionX4 + 1];
- m = data[ sectionX4 + 2];
-
- uint8_t secoffset8 = (uint8_t)(offset) / 2;
-
- uint16_t mx = m * secoffset8;
-
- int16_t y = mx + u.b;
- if( theta & 0x8000 ) y = -y;
-
- return y;
-}
-
-LIB8STATIC int16_t sin16_C( uint16_t theta )
-{
- static const uint16_t base[] =
- { 0, 6393, 12539, 18204, 23170, 27245, 30273, 32137 };
- static const uint8_t slope[] =
- { 49, 48, 44, 38, 31, 23, 14, 4 };
-
- uint16_t offset = (theta & 0x3FFF) >> 3; // 0..2047
- if( theta & 0x4000 ) offset = 2047 - offset;
-
- uint8_t section = offset / 256; // 0..7
- uint16_t b = base[section];
- uint8_t m = slope[section];
-
- uint8_t secoffset8 = (uint8_t)(offset) / 2;
-
- uint16_t mx = m * secoffset8;
- int16_t y = mx + b;
-
- if( theta & 0x8000 ) y = -y;
-
- return y;
-}
-
-LIB8STATIC int16_t cos16( uint16_t theta)
-{
- return sin16( theta + 16384);
-}
-
-///////////////////////////////////////////////////////////////////////
-
-// sin8 & cos8
-// Fast 8-bit approximations of sin(x) & cos(x).
-// Input angle is an unsigned int from 0-255.
-// Output is an unsigned int from 0 to 255.
-//
-// This approximation can vary to to 2%
-// from the floating point value you'd get by doing
-// float s = (sin( x ) * 128.0) + 128;
-//
-// Don't use this approximation for calculating the
-// "real" trigonometric calculations, but it's great
-// for art projects and LED displays.
-//
-// On Arduino/AVR, this approximation is more than
-// 20X faster than floating point sin(x) and cos(x)
-
-#if defined(__AVR__) && !defined(LIB8_ATTINY)
-#define sin8 sin8_avr
-#else
-#define sin8 sin8_C
-#endif
-
-
-const uint8_t b_m16_interleave[] = { 0, 49, 49, 41, 90, 27, 117, 10 };
-
-LIB8STATIC uint8_t sin8_avr( uint8_t theta)
-{
- uint8_t offset = theta;
-
- asm volatile(
- "sbrc %[theta],6 \n\t"
- "com %[offset] \n\t"
- : [theta] "+r" (theta), [offset] "+r" (offset)
- );
-
- offset &= 0x3F; // 0..63
-
- uint8_t secoffset = offset & 0x0F; // 0..15
- if( theta & 0x40) secoffset++;
-
- uint8_t m16; uint8_t b;
-
- uint8_t section = offset >> 4; // 0..3
- uint8_t s2 = section * 2;
-
- const uint8_t* p = b_m16_interleave;
- p += s2;
- b = *p;
- p++;
- m16 = *p;
-
- uint8_t mx;
- uint8_t xr1;
- asm volatile(
- "mul %[m16],%[secoffset] \n\t"
- "mov %[mx],r0 \n\t"
- "mov %[xr1],r1 \n\t"
- "eor r1, r1 \n\t"
- "swap %[mx] \n\t"
- "andi %[mx],0x0F \n\t"
- "swap %[xr1] \n\t"
- "andi %[xr1], 0xF0 \n\t"
- "or %[mx], %[xr1] \n\t"
- : [mx] "=r" (mx), [xr1] "=r" (xr1)
- : [m16] "r" (m16), [secoffset] "r" (secoffset)
- );
-
- int8_t y = mx + b;
- if( theta & 0x80 ) y = -y;
-
- y += 128;
-
- return y;
-}
-
-
-LIB8STATIC uint8_t sin8_C( uint8_t theta)
-{
- uint8_t offset = theta;
- if( theta & 0x40 ) {
- offset = (uint8_t)255 - offset;
- }
- offset &= 0x3F; // 0..63
-
- uint8_t secoffset = offset & 0x0F; // 0..15
- if( theta & 0x40) secoffset++;
-
- uint8_t section = offset >> 4; // 0..3
- uint8_t s2 = section * 2;
- const uint8_t* p = b_m16_interleave;
- p += s2;
- uint8_t b = *p;
- p++;
- uint8_t m16 = *p;
-
- uint8_t mx = (m16 * secoffset) >> 4;
-
- int8_t y = mx + b;
- if( theta & 0x80 ) y = -y;
-
- y += 128;
-
- return y;
-}
-
-
-LIB8STATIC uint8_t cos8( uint8_t theta)
-{
- return sin8( theta + 64);
-}
-
-
-///////////////////////////////////////////////////////////////////////
//
// memmove8, memcpy8, and memset8:
// alternatives to memmove, memcpy, and memset that are
@@ -1361,39 +432,51 @@ void * memset8 ( void * ptr, uint8_t value, uint16_t num ) __attribute__ ((noinl
// linear interpolation, such as could be used for Perlin noise, etc.
//
-// linear interpolation between two unsigned 8-bit values,
-// with 8-bit fraction
+// A note on the structure of the lerp functions:
+// The cases for b>a and b<=a are handled separately for
+// speed: without knowing the relative order of a and b,
+// the value (a-b) might be overflow the width of a or b,
+// and have to be promoted to a wider, slower type.
+// To avoid that, we separate the two cases, and are able
+// to do all the math in the same width as the arguments,
+// which is much faster and smaller on AVR.
+
+/// linear interpolation between two unsigned 8-bit values,
+/// with 8-bit fraction
LIB8STATIC uint8_t lerp8by8( uint8_t a, uint8_t b, fract8 frac)
{
- uint8_t delta = b - a;
- uint8_t scaled = scale8( delta, frac);
- uint8_t result = a + scaled;
+ uint8_t result;
+ if( b > a) {
+ uint8_t delta = b - a;
+ uint8_t scaled = scale8( delta, frac);
+ result = a + scaled;
+ } else {
+ uint8_t delta = a - b;
+ uint8_t scaled = scale8( delta, frac);
+ result = a - scaled;
+ }
return result;
}
-// linear interpolation between two unsigned 16-bit values,
-// with 16-bit fraction
+/// linear interpolation between two unsigned 16-bit values,
+/// with 16-bit fraction
LIB8STATIC uint16_t lerp16by16( uint16_t a, uint16_t b, fract16 frac)
{
- uint16_t delta = b - a;
- uint32_t prod = (uint32_t)delta * (uint32_t)frac;
- uint16_t scaled = prod >> 16;
- uint16_t result = a + scaled;
+ uint16_t result;
+ if( b > a ) {
+ uint16_t delta = b - a;
+ uint32_t scaled = scale16(delta, frac);
+ result = a + scaled;
+ } else {
+ uint16_t delta = a - b;
+ uint16_t scaled = scale16( delta, frac);
+ result = a - scaled;
+ }
return result;
}
-
-// A note on the structure of lerp16by8 (and lerp15by8) :
-// The cases for b>a and b<=a are handled separately for
-// speed: without knowing the relative order of a and b,
-// the value (a-b) might be a signed 17-bit value, which
-// would have to be stored in a 32-bit signed int and
-// processed as such. To avoid that, we separate the
-// two cases, and are able to do all the math with 16-bit
-// unsigned values, which is much faster and smaller on AVR.
-
-// linear interpolation between two unsigned 16-bit values,
-// with 8-bit fraction
+/// linear interpolation between two unsigned 16-bit values,
+/// with 8-bit fraction
LIB8STATIC uint16_t lerp16by8( uint16_t a, uint16_t b, fract8 frac)
{
uint16_t result;
@@ -1409,8 +492,8 @@ LIB8STATIC uint16_t lerp16by8( uint16_t a, uint16_t b, fract8 frac)
return result;
}
-// linear interpolation between two signed 15-bit values,
-// with 8-bit fraction
+/// linear interpolation between two signed 15-bit values,
+/// with 8-bit fraction
LIB8STATIC int16_t lerp15by8( int16_t a, int16_t b, fract8 frac)
{
int16_t result;
@@ -1426,8 +509,8 @@ LIB8STATIC int16_t lerp15by8( int16_t a, int16_t b, fract8 frac)
return result;
}
-// linear interpolation between two signed 15-bit values,
-// with 8-bit fraction
+/// linear interpolation between two signed 15-bit values,
+/// with 8-bit fraction
LIB8STATIC int16_t lerp15by16( int16_t a, int16_t b, fract16 frac)
{
int16_t result;
@@ -1443,20 +526,23 @@ LIB8STATIC int16_t lerp15by16( int16_t a, int16_t b, fract16 frac)
return result;
}
-// map8: map from one full-range 8-bit value into a narrower
-// range of 8-bit values, possibly a range of hues.
-//
-// E.g. map myValue into a hue in the range blue..purple..pink..red
-// hue = map8( myValue, HUE_BLUE, HUE_RED);
-//
-// Combines nicely with the waveform functions (like sin8, etc)
-// to produce continuous hue gradients back and forth:
-// hue = map8( sin8( myValue), HUE_BLUE, HUE_RED);
-//
-// Mathematically simiar to lerp8by8, but arguments are more
-// like Arduino's "map"; this function is similar to
-// map( in, 0, 255, rangeStart, rangeEnd)
-// but faster and specifically designed for 8-bit values.
+/// map8: map from one full-range 8-bit value into a narrower
+/// range of 8-bit values, possibly a range of hues.
+///
+/// E.g. map myValue into a hue in the range blue..purple..pink..red
+/// hue = map8( myValue, HUE_BLUE, HUE_RED);
+///
+/// Combines nicely with the waveform functions (like sin8, etc)
+/// to produce continuous hue gradients back and forth:
+///
+/// hue = map8( sin8( myValue), HUE_BLUE, HUE_RED);
+///
+/// Mathematically simiar to lerp8by8, but arguments are more
+/// like Arduino's "map"; this function is similar to
+///
+/// map( in, 0, 255, rangeStart, rangeEnd)
+///
+/// but faster and specifically designed for 8-bit values.
LIB8STATIC uint8_t map8( uint8_t in, uint8_t rangeStart, uint8_t rangeEnd)
{
uint8_t rangeWidth = rangeEnd - rangeStart;
@@ -1471,8 +557,8 @@ LIB8STATIC uint8_t map8( uint8_t in, uint8_t rangeStart, uint8_t rangeEnd)
// easing functions; see http://easings.net
//
-// ease8InOutQuad: 8-bit quadratic ease-in / ease-out function
-// Takes around 13 cycles on AVR
+/// ease8InOutQuad: 8-bit quadratic ease-in / ease-out function
+/// Takes around 13 cycles on AVR
LIB8STATIC uint8_t ease8InOutQuad( uint8_t i)
{
uint8_t j = i;
@@ -1488,8 +574,8 @@ LIB8STATIC uint8_t ease8InOutQuad( uint8_t i)
}
-// ease8InOutCubic: 8-bit cubic ease-in / ease-out function
-// Takes around 18 cycles on AVR
+/// ease8InOutCubic: 8-bit cubic ease-in / ease-out function
+/// Takes around 18 cycles on AVR
LIB8STATIC fract8 ease8InOutCubic( fract8 i)
{
uint8_t ii = scale8_LEAVING_R1_DIRTY( i, i);
@@ -1510,13 +596,13 @@ LIB8STATIC fract8 ease8InOutCubic( fract8 i)
return result;
}
-// ease8InOutApprox: fast, rough 8-bit ease-in/ease-out function
-// shaped approximately like 'ease8InOutCubic',
-// it's never off by more than a couple of percent
-// from the actual cubic S-curve, and it executes
-// more than twice as fast. Use when the cycles
-// are more important than visual smoothness.
-// Asm version takes around 7 cycles on AVR.
+/// ease8InOutApprox: fast, rough 8-bit ease-in/ease-out function
+/// shaped approximately like 'ease8InOutCubic',
+/// it's never off by more than a couple of percent
+/// from the actual cubic S-curve, and it executes
+/// more than twice as fast. Use when the cycles
+/// are more important than visual smoothness.
+/// Asm version takes around 7 cycles on AVR.
#if EASE8_C == 1
LIB8STATIC fract8 ease8InOutApprox( fract8 i)
@@ -1574,16 +660,16 @@ LIB8STATIC uint8_t ease8InOutApprox( fract8 i)
-// triwave8: triangle (sawtooth) wave generator. Useful for
-// turning a one-byte ever-increasing value into a
-// one-byte value that oscillates up and down.
-//
-// input output
-// 0..127 0..254 (positive slope)
-// 128..255 254..0 (negative slope)
-//
-// On AVR this function takes just three cycles.
-//
+/// triwave8: triangle (sawtooth) wave generator. Useful for
+/// turning a one-byte ever-increasing value into a
+/// one-byte value that oscillates up and down.
+///
+/// input output
+/// 0..127 0..254 (positive slope)
+/// 128..255 254..0 (negative slope)
+///
+/// On AVR this function takes just three cycles.
+///
LIB8STATIC uint8_t triwave8(uint8_t in)
{
if( in & 0x80) {
@@ -1603,56 +689,57 @@ LIB8STATIC uint8_t triwave8(uint8_t in)
// slightly different curve shapes.
//
-// quadwave8: quadratic waveform generator. Spends just a little more
-// time at the limits than 'sine' does.
+/// quadwave8: quadratic waveform generator. Spends just a little more
+/// time at the limits than 'sine' does.
LIB8STATIC uint8_t quadwave8(uint8_t in)
{
return ease8InOutQuad( triwave8( in));
}
-// cubicwave8: cubic waveform generator. Spends visibly more time
-// at the limits than 'sine' does.
+/// cubicwave8: cubic waveform generator. Spends visibly more time
+/// at the limits than 'sine' does.
LIB8STATIC uint8_t cubicwave8(uint8_t in)
{
return ease8InOutCubic( triwave8( in));
}
-
-
-// sqrt16: square root for 16-bit integers
-// About three times faster and five times smaller
-// than Arduino's general sqrt on AVR.
-LIB8STATIC uint8_t sqrt16(uint16_t x)
-{
- if( x <= 1) {
- return x;
- }
-
- uint8_t low = 1; // lower bound
- uint8_t hi, mid;
-
- if( x > 7904) {
- hi = 255;
+/// squarewave8: square wave generator. Useful for
+/// turning a one-byte ever-increasing value
+/// into a one-byte value that is either 0 or 255.
+/// The width of the output 'pulse' is
+/// determined by the pulsewidth argument:
+///
+///~~~
+/// If pulsewidth is 255, output is always 255.
+/// If pulsewidth < 255, then
+/// if input < pulsewidth then output is 255
+/// if input >= pulsewidth then output is 0
+///~~~
+///
+/// the output looking like:
+///
+///~~~
+/// 255 +--pulsewidth--+
+/// . | |
+/// 0 0 +--------(256-pulsewidth)--------
+///~~~
+///
+/// @param in
+/// @param pulsewidth
+/// @returns square wave output
+LIB8STATIC uint8_t squarewave8( uint8_t in, uint8_t pulsewidth=128)
+{
+ if( in < pulsewidth || (pulsewidth == 255)) {
+ return 255;
} else {
- hi = (x >> 5) + 8; // initial estimate for upper bound
+ return 0;
}
-
- do {
- mid = (low + hi) >> 1;
- if ((uint16_t)(mid * mid) > x) {
- hi = mid - 1;
- } else {
- if( mid == 255) {
- return 255;
- }
- low = mid + 1;
- }
- } while (hi >= low);
-
- return low - 1;
}
+
+
+/// Template class for represneting fractional ints.
template<class T, int F, int I> class q {
T i:I;
T f:F;
@@ -1676,9 +763,13 @@ template<class T, int F, int I> static int16_t operator*(int16_t v, q<T,F,I> & q
template<class T, int F, int I> static int operator*(int v, q<T,F,I> & q) { return q * v; }
#endif
+/// A 4.4 integer (4 bits integer, 4 bits fraction)
typedef q<uint8_t, 4,4> q44;
+/// A 6.2 integer (6 bits integer, 2 bits fraction)
typedef q<uint8_t, 6,2> q62;
+/// A 8.8 integer (8 bits integer, 8 bits fraction)
typedef q<uint16_t, 8,8> q88;
+/// A 12.4 integer (12 bits integer, 4 bits fraction)
typedef q<uint16_t, 12,4> q124;
@@ -1697,13 +788,18 @@ typedef q<uint16_t, 12,4> q124;
// per minute, rising from 0 to 65535, resetting to zero,
// rising up again, etc. The output of this function is
// suitable for feeding directly into sin16 and cos16.
-//
+// beat88( BPM88) is the same as beat16, except that the BPM88 argument
+// MUST be in Q8.8 fixed point format, e.g. 120BPM must
+// be specified as 120*256 = 30720.
// beatsin8( BPM, uint8_t low, uint8_t high) returns an 8-bit value that
// rises and falls in a sine wave, 'BPM' times per minute,
// between the values of 'low' and 'high'.
// beatsin16( BPM, uint16_t low, uint16_t high) returns a 16-bit value
// that rises and falls in a sine wave, 'BPM' times per
// minute, between the values of 'low' and 'high'.
+// beatsin88( BPM88, ...) is the same as beatsin16, except that the
+// BPM88 argument MUST be in Q8.8 fixed point format,
+// e.g. 120BPM must be specified as 120*256 = 30720.
//
// BPM can be supplied two ways. The simpler way of specifying BPM is as
// a simple 8-bit integer from 1-255, (e.g., "120").
@@ -1712,6 +808,7 @@ typedef q<uint16_t, 12,4> q124;
// an 8-bit fractional part. The easiest way to construct this is to multiply
// a floating point BPM value (e.g. 120.3) by 256, (e.g. resulting in 30796
// in this case), and pass that as the 16-bit BPM argument.
+// "BPM88" MUST always be specified in Q8.8 format.
//
// Originally designed to make an entire animation project pulse with brightness.
// For that effect, add this line just above your existing call to "FastLED.show()":
@@ -1730,21 +827,22 @@ typedef q<uint16_t, 12,4> q124;
// that provides similar functionality.
// You can also force use of the get_millisecond_timer function
// by #defining USE_GET_MILLISECOND_TIMER.
-#if defined(ARDUINO) && !defined(USE_GET_MILLISECOND_TIMER)
+#if (defined(ARDUINO) || defined(SPARK)) && !defined(USE_GET_MILLISECOND_TIMER)
// Forward declaration of Arduino function 'millis'.
uint32_t millis();
-#define GET_MILLIS (millis())
+#define GET_MILLIS millis
#else
uint32_t get_millisecond_timer();
-#define GET_MILLIS (get_millisecond_timer())
+#define GET_MILLIS get_millisecond_timer
#endif
-// beat16 generates a 16-bit 'sawtooth' wave at a given BPM
-LIB8STATIC uint16_t beat16( accum88 beats_per_minute)
+// beat16 generates a 16-bit 'sawtooth' wave at a given BPM,
+/// with BPM specified in Q8.8 fixed-point format; e.g.
+/// for this function, 120 BPM MUST BE specified as
+/// 120*256 = 30720.
+/// If you just want to specify "120", use beat16 or beat8.
+LIB8STATIC uint16_t beat88( accum88 beats_per_minute_88, uint32_t timebase = 0)
{
- // Convert simple 8-bit BPM's to full Q8.8 accum88's if needed
- if( beats_per_minute < 256) beats_per_minute <<= 8;
-
// BPM is 'beats per minute', or 'beats per 60000ms'.
// To avoid using the (slower) division operator, we
// want to convert 'beats per 60000ms' to 'beats per 65536ms',
@@ -1753,35 +851,60 @@ LIB8STATIC uint16_t beat16( accum88 beats_per_minute)
// The ratio 65536:60000 is 279.620266667:256; we'll call it 280:256.
// The conversion is accurate to about 0.05%, more or less,
// e.g. if you ask for "120 BPM", you'll get about "119.93".
- // If you need more precision than that, you can specify a
- // sixteen-bit BPM value in Q8.8 fixed-point (an 'accum88').
- return ((GET_MILLIS) * beats_per_minute * 280) >> 16;
+ return (((GET_MILLIS()) - timebase) * beats_per_minute_88 * 280) >> 16;
}
-// beat8 generates an 8-bit 'sawtooth' wave at a given BPM
-LIB8STATIC uint8_t beat8( accum88 beats_per_minute)
+/// beat16 generates a 16-bit 'sawtooth' wave at a given BPM
+LIB8STATIC uint16_t beat16( accum88 beats_per_minute, uint32_t timebase = 0)
{
- return beat16( beats_per_minute) >> 8;
+ // Convert simple 8-bit BPM's to full Q8.8 accum88's if needed
+ if( beats_per_minute < 256) beats_per_minute <<= 8;
+ return beat88(beats_per_minute, timebase);
}
-// beatsin16 generates a 16-bit sine wave at a given BPM,
-// that oscillates within a given range.
-LIB8STATIC uint16_t beatsin16( accum88 beats_per_minute, uint16_t lowest = 0, uint16_t highest = 65535)
+/// beat8 generates an 8-bit 'sawtooth' wave at a given BPM
+LIB8STATIC uint8_t beat8( accum88 beats_per_minute, uint32_t timebase = 0)
{
- uint16_t beat = beat16( beats_per_minute);
- uint16_t beatsin = (sin16( beat) + 32768);
+ return beat16( beats_per_minute, timebase) >> 8;
+}
+
+/// beatsin88 generates a 16-bit sine wave at a given BPM,
+/// that oscillates within a given range.
+/// For this function, BPM MUST BE SPECIFIED as
+/// a Q8.8 fixed-point value; e.g. 120BPM must be
+/// specified as 120*256 = 30720.
+/// If you just want to specify "120", use beatsin16 or beatsin8.
+LIB8STATIC uint16_t beatsin88( accum88 beats_per_minute_88, uint16_t lowest = 0, uint16_t highest = 65535,
+ uint32_t timebase = 0, uint16_t phase_offset = 0)
+{
+ uint16_t beat = beat88( beats_per_minute_88, timebase);
+ uint16_t beatsin = (sin16( beat + phase_offset) + 32768);
uint16_t rangewidth = highest - lowest;
uint16_t scaledbeat = scale16( beatsin, rangewidth);
uint16_t result = lowest + scaledbeat;
return result;
}
-// beatsin8 generates an 8-bit sine wave at a given BPM,
-// that oscillates within a given range.
-LIB8STATIC uint8_t beatsin8( accum88 beats_per_minute, uint8_t lowest = 0, uint8_t highest = 255)
+/// beatsin16 generates a 16-bit sine wave at a given BPM,
+/// that oscillates within a given range.
+LIB8STATIC uint16_t beatsin16( accum88 beats_per_minute, uint16_t lowest = 0, uint16_t highest = 65535,
+ uint32_t timebase = 0, uint16_t phase_offset = 0)
{
- uint8_t beat = beat8( beats_per_minute);
- uint8_t beatsin = sin8( beat);
+ uint16_t beat = beat16( beats_per_minute, timebase);
+ uint16_t beatsin = (sin16( beat + phase_offset) + 32768);
+ uint16_t rangewidth = highest - lowest;
+ uint16_t scaledbeat = scale16( beatsin, rangewidth);
+ uint16_t result = lowest + scaledbeat;
+ return result;
+}
+
+/// beatsin8 generates an 8-bit sine wave at a given BPM,
+/// that oscillates within a given range.
+LIB8STATIC uint8_t beatsin8( accum88 beats_per_minute, uint8_t lowest = 0, uint8_t highest = 255,
+ uint32_t timebase = 0, uint8_t phase_offset = 0)
+{
+ uint8_t beat = beat8( beats_per_minute, timebase);
+ uint8_t beatsin = sin8( beat + phase_offset);
uint8_t rangewidth = highest - lowest;
uint8_t scaledbeat = scale8( beatsin, rangewidth);
uint8_t result = lowest + scaledbeat;
@@ -1789,4 +912,172 @@ LIB8STATIC uint8_t beatsin8( accum88 beats_per_minute, uint8_t lowest = 0, uint8
}
+/// Return the current seconds since boot in a 16-bit value. Used as part of the
+/// "every N time-periods" mechanism
+LIB8STATIC uint16_t seconds16()
+{
+ uint32_t ms = GET_MILLIS();
+ uint16_t s16;
+ s16 = ms / 1000;
+ return s16;
+}
+
+/// Return the current minutes since boot in a 16-bit value. Used as part of the
+/// "every N time-periods" mechanism
+LIB8STATIC uint16_t minutes16()
+{
+ uint32_t ms = GET_MILLIS();
+ uint16_t m16;
+ m16 = (ms / (60000L)) & 0xFFFF;
+ return m16;
+}
+
+/// Return the current hours since boot in an 8-bit value. Used as part of the
+/// "every N time-periods" mechanism
+LIB8STATIC uint8_t hours8()
+{
+ uint32_t ms = GET_MILLIS();
+ uint8_t h8;
+ h8 = (ms / (3600000L)) & 0xFF;
+ return h8;
+}
+
+
+/// Helper routine to divide a 32-bit value by 1024, returning
+/// only the low 16 bits. You'd think this would be just
+/// result = (in32 >> 10) & 0xFFFF;
+/// and on ARM, that's what you want and all is well.
+/// But on AVR that code turns into a loop that executes
+/// a four-byte shift ten times: 40 shifts in all, plus loop
+/// overhead. This routine gets exactly the same result with
+/// just six shifts (vs 40), and no loop overhead.
+/// Used to convert millis to 'binary seconds' aka bseconds:
+/// one bsecond == 1024 millis.
+LIB8STATIC uint16_t div1024_32_16( uint32_t in32)
+{
+ uint16_t out16;
+#if defined(__AVR__)
+ asm volatile (
+ " lsr %D[in] \n\t"
+ " ror %C[in] \n\t"
+ " ror %B[in] \n\t"
+ " lsr %D[in] \n\t"
+ " ror %C[in] \n\t"
+ " ror %B[in] \n\t"
+ " mov %B[out],%C[in] \n\t"
+ " mov %A[out],%B[in] \n\t"
+ : [in] "+r" (in32),
+ [out] "=r" (out16)
+ );
+#else
+ out16 = (in32 >> 10) & 0xFFFF;
+#endif
+ return out16;
+}
+
+/// bseconds16 returns the current time-since-boot in
+/// "binary seconds", which are actually 1024/1000 of a
+/// second long.
+LIB8STATIC uint16_t bseconds16()
+{
+ uint32_t ms = GET_MILLIS();
+ uint16_t s16;
+ s16 = div1024_32_16( ms);
+ return s16;
+}
+
+
+// Classes to implement "Every N Milliseconds", "Every N Seconds",
+// "Every N Minutes", "Every N Hours", and "Every N BSeconds".
+#if 1
+#define INSTANTIATE_EVERY_N_TIME_PERIODS(NAME,TIMETYPE,TIMEGETTER) \
+class NAME { \
+public: \
+ TIMETYPE mPrevTrigger; \
+ TIMETYPE mPeriod; \
+ \
+ NAME() { reset(); mPeriod = 1; }; \
+ NAME(TIMETYPE period) { reset(); setPeriod(period); }; \
+ void setPeriod( TIMETYPE period) { mPeriod = period; }; \
+ TIMETYPE getTime() { return (TIMETYPE)(TIMEGETTER()); }; \
+ TIMETYPE getPeriod() { return mPeriod; }; \
+ TIMETYPE getElapsed() { return getTime() - mPrevTrigger; } \
+ TIMETYPE getRemaining() { return mPeriod - getElapsed(); } \
+ TIMETYPE getLastTriggerTime() { return mPrevTrigger; } \
+ bool ready() { \
+ bool isReady = (getElapsed() >= mPeriod); \
+ if( isReady ) { reset(); } \
+ return isReady; \
+ } \
+ void reset() { mPrevTrigger = getTime(); }; \
+ void trigger() { mPrevTrigger = getTime() - mPeriod; }; \
+ \
+ operator bool() { return ready(); } \
+};
+INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNMillis,uint32_t,GET_MILLIS);
+INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNSeconds,uint16_t,seconds16);
+INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNBSeconds,uint16_t,bseconds16);
+INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNMinutes,uint16_t,minutes16);
+INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNHours,uint8_t,hours8);
+#else
+
+// Under C++11 rules, we would be allowed to use not-external
+// -linkage-type symbols as template arguments,
+// e.g., LIB8STATIC seconds16, and we'd be able to use these
+// templates as shown below.
+// However, under C++03 rules, we cannot do that, and thus we
+// have to resort to the preprocessor to 'instantiate' 'templates',
+// as handled above.
+template<typename timeType,timeType (*timeGetter)()>
+class CEveryNTimePeriods {
+public:
+ timeType mPrevTrigger;
+ timeType mPeriod;
+
+ CEveryNTimePeriods() { reset(); mPeriod = 1; };
+ CEveryNTimePeriods(timeType period) { reset(); setPeriod(period); };
+ void setPeriod( timeType period) { mPeriod = period; };
+ timeType getTime() { return (timeType)(timeGetter()); };
+ timeType getPeriod() { return mPeriod; };
+ timeType getElapsed() { return getTime() - mPrevTrigger; }
+ timeType getRemaining() { return mPeriod - getElapsed(); }
+ timeType getLastTriggerTime() { return mPrevTrigger; }
+ bool ready() {
+ bool isReady = (getElapsed() >= mPeriod);
+ if( isReady ) { reset(); }
+ return isReady;
+ }
+ void reset() { mPrevTrigger = getTime(); };
+ void trigger() { mPrevTrigger = getTime() - mPeriod; };
+
+ operator bool() { return ready(); }
+};
+typedef CEveryNTimePeriods<uint16_t,seconds16> CEveryNSeconds;
+typedef CEveryNTimePeriods<uint16_t,bseconds16> CEveryNBSeconds;
+typedef CEveryNTimePeriods<uint32_t,millis> CEveryNMillis;
+typedef CEveryNTimePeriods<uint16_t,minutes16> CEveryNMinutes;
+typedef CEveryNTimePeriods<uint8_t,hours8> CEveryNHours;
+#endif
+
+
+#define CONCAT_HELPER( x, y ) x##y
+#define CONCAT_MACRO( x, y ) CONCAT_HELPER( x, y )
+#define EVERY_N_MILLIS(N) EVERY_N_MILLIS_I(CONCAT_MACRO(PER, __COUNTER__ ),N)
+#define EVERY_N_MILLIS_I(NAME,N) static CEveryNMillis NAME(N); if( NAME )
+#define EVERY_N_SECONDS(N) EVERY_N_SECONDS_I(CONCAT_MACRO(PER, __COUNTER__ ),N)
+#define EVERY_N_SECONDS_I(NAME,N) static CEveryNSeconds NAME(N); if( NAME )
+#define EVERY_N_BSECONDS(N) EVERY_N_BSECONDS_I(CONCAT_MACRO(PER, __COUNTER__ ),N)
+#define EVERY_N_BSECONDS_I(NAME,N) static CEveryNBSeconds NAME(N); if( NAME )
+#define EVERY_N_MINUTES(N) EVERY_N_MINUTES_I(CONCAT_MACRO(PER, __COUNTER__ ),N)
+#define EVERY_N_MINUTES_I(NAME,N) static CEveryNMinutes NAME(N); if( NAME )
+#define EVERY_N_HOURS(N) EVERY_N_HOURS_I(CONCAT_MACRO(PER, __COUNTER__ ),N)
+#define EVERY_N_HOURS_I(NAME,N) static CEveryNHours NAME(N); if( NAME )
+
+#define CEveryNMilliseconds CEveryNMillis
+#define EVERY_N_MILLISECONDS(N) EVERY_N_MILLIS(N)
+#define EVERY_N_MILLISECONDS_I(NAME,N) EVERY_N_MILLIS_I(NAME,N)
+
+FASTLED_NAMESPACE_END
+///@}
+
#endif
diff --git a/lib8tion/math8.h b/lib8tion/math8.h
new file mode 100644
index 00000000..9e87adc4
--- /dev/null
+++ b/lib8tion/math8.h
@@ -0,0 +1,357 @@
+#ifndef __INC_LIB8TION_MATH_H
+#define __INC_LIB8TION_MATH_H
+
+///@ingroup lib8tion
+
+///@defgroup Math Basic math operations
+/// Fast, efficient 8-bit math functions specifically
+/// designed for high-performance LED programming.
+///
+/// Because of the AVR(Arduino) and ARM assembly language
+/// implementations provided, using these functions often
+/// results in smaller and faster code than the equivalent
+/// program using plain "C" arithmetic and logic.
+///@{
+
+
+/// add one byte to another, saturating at 0xFF
+/// @param i - first byte to add
+/// @param j - second byte to add
+/// @returns the sum of i & j, capped at 0xFF
+LIB8STATIC uint8_t qadd8( uint8_t i, uint8_t j)
+{
+#if QADD8_C == 1
+ unsigned int t = i + j;
+ if( t > 255) t = 255;
+ return t;
+#elif QADD8_AVRASM == 1
+ asm volatile(
+ /* First, add j to i, conditioning the C flag */
+ "add %0, %1 \n\t"
+
+ /* Now test the C flag.
+ If C is clear, we branch around a load of 0xFF into i.
+ If C is set, we go ahead and load 0xFF into i.
+ */
+ "brcc L_%= \n\t"
+ "ldi %0, 0xFF \n\t"
+ "L_%=: "
+ : "+a" (i)
+ : "a" (j) );
+ return i;
+#elif QADD8_ARM_DSP_ASM == 1
+ asm volatile( "uqadd8 %0, %0, %1" : "+r" (i) : "r" (j));
+ return i;
+#else
+#error "No implementation for qadd8 available."
+#endif
+}
+
+/// Add one byte to another, saturating at 0x7F
+/// @param i - first byte to add
+/// @param j - second byte to add
+/// @returns the sum of i & j, capped at 0xFF
+LIB8STATIC int8_t qadd7( int8_t i, int8_t j)
+{
+#if QADD7_C == 1
+ int16_t t = i + j;
+ if( t > 127) t = 127;
+ return t;
+#elif QADD7_AVRASM == 1
+ asm volatile(
+ /* First, add j to i, conditioning the V flag */
+ "add %0, %1 \n\t"
+
+ /* Now test the V flag.
+ If V is clear, we branch around a load of 0x7F into i.
+ If V is set, we go ahead and load 0x7F into i.
+ */
+ "brvc L_%= \n\t"
+ "ldi %0, 0x7F \n\t"
+ "L_%=: "
+ : "+a" (i)
+ : "a" (j) );
+
+ return i;
+#elif QADD7_ARM_DSP_ASM == 1
+ asm volatile( "qadd8 %0, %0, %1" : "+r" (i) : "r" (j));
+ return i;
+#else
+#error "No implementation for qadd7 available."
+#endif
+}
+
+/// subtract one byte from another, saturating at 0x00
+/// @returns i - j with a floor of 0
+LIB8STATIC uint8_t qsub8( uint8_t i, uint8_t j)
+{
+#if QSUB8_C == 1
+ int t = i - j;
+ if( t < 0) t = 0;
+ return t;
+#elif QSUB8_AVRASM == 1
+
+ asm volatile(
+ /* First, subtract j from i, conditioning the C flag */
+ "sub %0, %1 \n\t"
+
+ /* Now test the C flag.
+ If C is clear, we branch around a load of 0x00 into i.
+ If C is set, we go ahead and load 0x00 into i.
+ */
+ "brcc L_%= \n\t"
+ "ldi %0, 0x00 \n\t"
+ "L_%=: "
+ : "+a" (i)
+ : "a" (j) );
+
+ return i;
+#else
+#error "No implementation for qsub8 available."
+#endif
+}
+
+/// add one byte to another, with one byte result
+LIB8STATIC uint8_t add8( uint8_t i, uint8_t j)
+{
+#if ADD8_C == 1
+ int t = i + j;
+ return t;
+#elif ADD8_AVRASM == 1
+ // Add j to i, period.
+ asm volatile( "add %0, %1" : "+a" (i) : "a" (j));
+ return i;
+#else
+#error "No implementation for add8 available."
+#endif
+}
+
+
+/// subtract one byte from another, 8-bit result
+LIB8STATIC uint8_t sub8( uint8_t i, uint8_t j)
+{
+#if SUB8_C == 1
+ int t = i - j;
+ return t;
+#elif SUB8_AVRASM == 1
+ // Subtract j from i, period.
+ asm volatile( "sub %0, %1" : "+a" (i) : "a" (j));
+ return i;
+#else
+#error "No implementation for sub8 available."
+#endif
+}
+
+/// Calculate an integer average of two unsigned
+/// 8-bit integer values (uint8_t).
+/// Fractional results are rounded down, e.g. avg8(20,41) = 30
+LIB8STATIC uint8_t avg8( uint8_t i, uint8_t j)
+{
+#if AVG8_C == 1
+ return (i + j) >> 1;
+#elif AVG8_AVRASM == 1
+ asm volatile(
+ /* First, add j to i, 9th bit overflows into C flag */
+ "add %0, %1 \n\t"
+ /* Divide by two, moving C flag into high 8th bit */
+ "ror %0 \n\t"
+ : "+a" (i)
+ : "a" (j) );
+ return i;
+#else
+#error "No implementation for avg8 available."
+#endif
+}
+
+
+/// Calculate an integer average of two signed 7-bit
+/// integers (int8_t)
+/// If the first argument is even, result is rounded down.
+/// If the first argument is odd, result is result up.
+LIB8STATIC int8_t avg7( int8_t i, int8_t j)
+{
+#if AVG7_C == 1
+ return ((i + j) >> 1) + (i & 0x1);
+#elif AVG7_AVRASM == 1
+ asm volatile(
+ "asr %1 \n\t"
+ "asr %0 \n\t"
+ "adc %0, %1 \n\t"
+ : "+a" (i)
+ : "a" (j) );
+ return i;
+#else
+#error "No implementation for avg7 available."
+#endif
+}
+
+/// Calculate the remainder of one unsigned 8-bit
+/// value divided by anoter, aka A % M.
+/// Implemented by repeated subtraction, which is
+/// very compact, and very fast if A is 'probably'
+/// less than M. If A is a large multiple of M,
+/// the loop has to execute multiple times. However,
+/// even in that case, the loop is only two
+/// instructions long on AVR, i.e., quick.
+LIB8STATIC uint8_t mod8( uint8_t a, uint8_t m)
+{
+#if defined(__AVR__)
+ asm volatile (
+ "L_%=: sub %[a],%[m] \n\t"
+ " brcc L_%= \n\t"
+ " add %[a],%[m] \n\t"
+ : [a] "+r" (a)
+ : [m] "r" (m)
+ );
+#else
+ while( a >= m) a -= m;
+#endif
+ return a;
+}
+
+/// Add two numbers, and calculate the modulo
+/// of the sum and a third number, M.
+/// In other words, it returns (A+B) % M.
+/// It is designed as a compact mechanism for
+/// incrementing a 'mode' switch and wrapping
+/// around back to 'mode 0' when the switch
+/// goes past the end of the available range.
+/// e.g. if you have seven modes, this switches
+/// to the next one and wraps around if needed:
+/// mode = addmod8( mode, 1, 7);
+/// See 'mod8' for notes on performance.
+LIB8STATIC uint8_t addmod8( uint8_t a, uint8_t b, uint8_t m)
+{
+#if defined(__AVR__)
+ asm volatile (
+ " add %[a],%[b] \n\t"
+ "L_%=: sub %[a],%[m] \n\t"
+ " brcc L_%= \n\t"
+ " add %[a],%[m] \n\t"
+ : [a] "+r" (a)
+ : [b] "r" (b), [m] "r" (m)
+ );
+#else
+ a += b;
+ while( a >= m) a -= m;
+#endif
+ return a;
+}
+
+/// 8x8 bit multiplication, with 8 bit result
+LIB8STATIC uint8_t mul8( uint8_t i, uint8_t j)
+{
+#if MUL8_C == 1
+ return ((int)i * (int)(j) ) & 0xFF;
+#elif MUL8_AVRASM == 1
+ asm volatile(
+ /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
+ "mul %0, %1 \n\t"
+ /* Extract the LOW 8-bits (r0) */
+ "mov %0, r0 \n\t"
+ /* Restore r1 to "0"; it's expected to always be that */
+ "clr __zero_reg__ \n\t"
+ : "+a" (i)
+ : "a" (j)
+ : "r0", "r1");
+
+ return i;
+#else
+#error "No implementation for mul8 available."
+#endif
+}
+
+
+/// saturating 8x8 bit multiplication, with 8 bit result
+/// @returns the product of i * j, capping at 0xFF
+LIB8STATIC uint8_t qmul8( uint8_t i, uint8_t j)
+{
+#if QMUL8_C == 1
+ int p = ((int)i * (int)(j) );
+ if( p > 255) p = 255;
+ return p;
+#elif QMUL8_AVRASM == 1
+ asm volatile(
+ /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
+ " mul %0, %1 \n\t"
+ /* If high byte of result is zero, all is well. */
+ " tst r1 \n\t"
+ " breq Lnospill_%= \n\t"
+ /* If high byte of result > 0, saturate low byte to 0xFF */
+ " ldi %0,0xFF \n\t"
+ " rjmp Ldone_%= \n\t"
+ "Lnospill_%=: \n\t"
+ /* Extract the LOW 8-bits (r0) */
+ " mov %0, r0 \n\t"
+ "Ldone_%=: \n\t"
+ /* Restore r1 to "0"; it's expected to always be that */
+ " clr __zero_reg__ \n\t"
+ : "+a" (i)
+ : "a" (j)
+ : "r0", "r1");
+
+ return i;
+#else
+#error "No implementation for qmul8 available."
+#endif
+}
+
+
+/// take abs() of a signed 8-bit uint8_t
+LIB8STATIC int8_t abs8( int8_t i)
+{
+#if ABS8_C == 1
+ if( i < 0) i = -i;
+ return i;
+#elif ABS8_AVRASM == 1
+
+
+ asm volatile(
+ /* First, check the high bit, and prepare to skip if it's clear */
+ "sbrc %0, 7 \n"
+
+ /* Negate the value */
+ "neg %0 \n"
+
+ : "+r" (i) : "r" (i) );
+ return i;
+#else
+#error "No implementation for abs8 available."
+#endif
+}
+
+/// square root for 16-bit integers
+/// About three times faster and five times smaller
+/// than Arduino's general sqrt on AVR.
+LIB8STATIC uint8_t sqrt16(uint16_t x)
+{
+ if( x <= 1) {
+ return x;
+ }
+
+ uint8_t low = 1; // lower bound
+ uint8_t hi, mid;
+
+ if( x > 7904) {
+ hi = 255;
+ } else {
+ hi = (x >> 5) + 8; // initial estimate for upper bound
+ }
+
+ do {
+ mid = (low + hi) >> 1;
+ if ((uint16_t)(mid * mid) > x) {
+ hi = mid - 1;
+ } else {
+ if( mid == 255) {
+ return 255;
+ }
+ low = mid + 1;
+ }
+ } while (hi >= low);
+
+ return low - 1;
+}
+
+///@}
+#endif
diff --git a/lib8tion/random8.h b/lib8tion/random8.h
new file mode 100644
index 00000000..1cd82dd4
--- /dev/null
+++ b/lib8tion/random8.h
@@ -0,0 +1,94 @@
+#ifndef __INC_LIB8TION_RANDOM_H
+#define __INC_LIB8TION_RANDOM_H
+///@ingroup lib8tion
+
+///@defgroup Random Fast random number generators
+/// Fast 8- and 16- bit unsigned random numbers.
+/// Significantly faster than Arduino random(), but
+/// also somewhat less random. You can add entropy.
+///@{
+
+// X(n+1) = (2053 * X(n)) + 13849)
+#define FASTLED_RAND16_2053 ((uint16_t)(2053))
+#define FASTLED_RAND16_13849 ((uint16_t)(13849))
+
+/// random number seed
+extern uint16_t rand16seed;// = RAND16_SEED;
+
+/// Generate an 8-bit random number
+LIB8STATIC uint8_t random8()
+{
+ rand16seed = (rand16seed * FASTLED_RAND16_2053) + FASTLED_RAND16_13849;
+ // return the sum of the high and low bytes, for better
+ // mixing and non-sequential correlation
+ return (uint8_t)(((uint8_t)(rand16seed & 0xFF)) +
+ ((uint8_t)(rand16seed >> 8)));
+}
+
+/// Generate a 16 bit random number
+LIB8STATIC uint16_t random16()
+{
+ rand16seed = (rand16seed * FASTLED_RAND16_2053) + FASTLED_RAND16_13849;
+ return rand16seed;
+}
+
+/// Generate an 8-bit random number between 0 and lim
+/// @param lim the upper bound for the result
+LIB8STATIC uint8_t random8(uint8_t lim)
+{
+ uint8_t r = random8();
+ r = scale8( r, lim);
+ return r;
+}
+
+/// Generate an 8-bit random number in the given range
+/// @param min the lower bound for the random number
+/// @param lim the upper bound for the random number
+LIB8STATIC uint8_t random8(uint8_t min, uint8_t lim)
+{
+ uint8_t delta = lim - min;
+ uint8_t r = random8(delta) + min;
+ return r;
+}
+
+/// Generate an 16-bit random number between 0 and lim
+/// @param lim the upper bound for the result
+LIB8STATIC uint16_t random16( uint16_t lim)
+{
+ uint16_t r = random16();
+ uint32_t p = (uint32_t)lim * (uint32_t)r;
+ r = p >> 16;
+ return r;
+}
+
+/// Generate an 16-bit random number in the given range
+/// @param min the lower bound for the random number
+/// @param lim the upper bound for the random number
+LIB8STATIC uint16_t random16( uint16_t min, uint16_t lim)
+{
+ uint16_t delta = lim - min;
+ uint16_t r = random16( delta) + min;
+ return r;
+}
+
+/// Set the 16-bit seed used for the random number generator
+LIB8STATIC void random16_set_seed( uint16_t seed)
+{
+ rand16seed = seed;
+}
+
+/// Get the current seed value for the random number generator
+LIB8STATIC uint16_t random16_get_seed()
+{
+ return rand16seed;
+}
+
+/// Add entropy into the random number generator
+LIB8STATIC void random16_add_entropy( uint16_t entropy)
+{
+ rand16seed += entropy;
+}
+
+///@}
+
+#endif
diff --git a/lib8tion/scale8.h b/lib8tion/scale8.h
new file mode 100644
index 00000000..239e9dea
--- /dev/null
+++ b/lib8tion/scale8.h
@@ -0,0 +1,511 @@
+#ifndef __INC_LIB8TION_SCALE_H
+#define __INC_LIB8TION_SCALE_H
+
+///@ingroup lib8tion
+
+///@defgroup Scaling Scaling functions
+/// Fast, efficient 8-bit scaling functions specifically
+/// designed for high-performance LED programming.
+///
+/// Because of the AVR(Arduino) and ARM assembly language
+/// implementations provided, using these functions often
+/// results in smaller and faster code than the equivalent
+/// program using plain "C" arithmetic and logic.
+///@{
+
+/// scale one byte by a second one, which is treated as
+/// the numerator of a fraction whose denominator is 256
+/// In other words, it computes i * (scale / 256)
+/// 4 clocks AVR with MUL, 2 clocks ARM
+LIB8STATIC uint8_t scale8( uint8_t i, fract8 scale)
+{
+#if SCALE8_C == 1
+ return ((uint16_t)i * (uint16_t)(scale) ) >> 8;
+#elif SCALE8_AVRASM == 1
+#if defined(LIB8_ATTINY)
+ uint8_t work=0;
+ uint8_t cnt=0x80;
+ asm volatile(
+ "LOOP_%=: \n\t"
+ /*" sbrc %[scale], 0 \n\t"
+ " add %[work], %[i] \n\t"
+ " ror %[work] \n\t"
+ " lsr %[scale] \n\t"
+ " clc \n\t"*/
+ " sbrc %[scale], 0 \n\t"
+ " add %[work], %[i] \n\t"
+ " ror %[work] \n\t"
+ " lsr %[scale] \n\t"
+ " lsr %[cnt] \n\t"
+ "brcc LOOP_%="
+ : [work] "+r" (work), [cnt] "+r" (cnt)
+ : [scale] "r" (scale), [i] "r" (i)
+ :
+ );
+ return work;
+#else
+ asm volatile(
+ /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
+ "mul %0, %1 \n\t"
+ /* Move the high 8-bits of the product (r1) back to i */
+ "mov %0, r1 \n\t"
+ /* Restore r1 to "0"; it's expected to always be that */
+ "clr __zero_reg__ \n\t"
+
+ : "+a" (i) /* writes to i */
+ : "a" (scale) /* uses scale */
+ : "r0", "r1" /* clobbers r0, r1 */ );
+
+ /* Return the result */
+ return i;
+#endif
+#else
+#error "No implementation for scale8 available."
+#endif
+}
+
+
+/// The "video" version of scale8 guarantees that the output will
+/// be only be zero if one or both of the inputs are zero. If both
+/// inputs are non-zero, the output is guaranteed to be non-zero.
+/// This makes for better 'video'/LED dimming, at the cost of
+/// several additional cycles.
+LIB8STATIC uint8_t scale8_video( uint8_t i, fract8 scale)
+{
+#if SCALE8_C == 1 || defined(LIB8_ATTINY)
+ uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
+ // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+ // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
+ return j;
+#elif SCALE8_AVRASM == 1
+ uint8_t j=0;
+ asm volatile(
+ " tst %[i]\n\t"
+ " breq L_%=\n\t"
+ " mul %[i], %[scale]\n\t"
+ " mov %[j], r1\n\t"
+ " clr __zero_reg__\n\t"
+ " cpse %[scale], r1\n\t"
+ " subi %[j], 0xFF\n\t"
+ "L_%=: \n\t"
+ : [j] "+a" (j)
+ : [i] "a" (i), [scale] "a" (scale)
+ : "r0", "r1");
+
+ return j;
+ // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+ // asm volatile(
+ // " tst %0 \n"
+ // " breq L_%= \n"
+ // " mul %0, %1 \n"
+ // " mov %0, r1 \n"
+ // " add %0, %2 \n"
+ // " clr __zero_reg__ \n"
+ // "L_%=: \n"
+
+ // : "+a" (i)
+ // : "a" (scale), "a" (nonzeroscale)
+ // : "r0", "r1");
+
+ // // Return the result
+ // return i;
+#else
+#error "No implementation for scale8_video available."
+#endif
+}
+
+
+/// This version of scale8 does not clean up the R1 register on AVR
+/// If you are doing several 'scale8's in a row, use this, and
+/// then explicitly call cleanup_R1.
+LIB8STATIC uint8_t scale8_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
+{
+#if SCALE8_C == 1
+ return ((int)i * (int)(scale) ) >> 8;
+#elif SCALE8_AVRASM == 1
+ asm volatile(
+ /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
+ "mul %0, %1 \n\t"
+ /* Move the high 8-bits of the product (r1) back to i */
+ "mov %0, r1 \n\t"
+ /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */
+ /* "clr __zero_reg__ \n\t" */
+
+ : "+a" (i) /* writes to i */
+ : "a" (scale) /* uses scale */
+ : "r0", "r1" /* clobbers r0, r1 */ );
+
+ // Return the result
+ return i;
+#else
+#error "No implementation for scale8_LEAVING_R1_DIRTY available."
+#endif
+}
+
+/// In place modifying version of scale8, also this version of nscale8 does not
+/// clean up the R1 register on AVR
+/// If you are doing several 'scale8's in a row, use this, and
+/// then explicitly call cleanup_R1.
+
+LIB8STATIC void nscale8_LEAVING_R1_DIRTY( uint8_t& i, fract8 scale)
+{
+#if SCALE8_C == 1
+ i = ((int)i * (int)(scale) ) >> 8;
+#elif SCALE8_AVRASM == 1
+ asm volatile(
+ /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
+ "mul %0, %1 \n\t"
+ /* Move the high 8-bits of the product (r1) back to i */
+ "mov %0, r1 \n\t"
+ /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */
+ /* "clr __zero_reg__ \n\t" */
+
+ : "+a" (i) /* writes to i */
+ : "a" (scale) /* uses scale */
+ : "r0", "r1" /* clobbers r0, r1 */ );
+#else
+#error "No implementation for nscale8_LEAVING_R1_DIRTY available."
+#endif
+}
+
+
+/// This version of scale8_video does not clean up the R1 register on AVR
+/// If you are doing several 'scale8_video's in a row, use this, and
+/// then explicitly call cleanup_R1.
+LIB8STATIC uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
+{
+#if SCALE8_C == 1 || defined(LIB8_ATTINY)
+ uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
+ // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+ // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
+ return j;
+#elif SCALE8_AVRASM == 1
+ uint8_t j=0;
+ asm volatile(
+ " tst %[i]\n\t"
+ " breq L_%=\n\t"
+ " mul %[i], %[scale]\n\t"
+ " mov %[j], r1\n\t"
+ " breq L_%=\n\t"
+ " subi %[j], 0xFF\n\t"
+ "L_%=: \n\t"
+ : [j] "+a" (j)
+ : [i] "a" (i), [scale] "a" (scale)
+ : "r0", "r1");
+
+ return j;
+ // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+ // asm volatile(
+ // " tst %0 \n"
+ // " breq L_%= \n"
+ // " mul %0, %1 \n"
+ // " mov %0, r1 \n"
+ // " add %0, %2 \n"
+ // " clr __zero_reg__ \n"
+ // "L_%=: \n"
+
+ // : "+a" (i)
+ // : "a" (scale), "a" (nonzeroscale)
+ // : "r0", "r1");
+
+ // // Return the result
+ // return i;
+#else
+#error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
+#endif
+}
+
+/// In place modifying version of scale8_video, also this version of nscale8_video
+/// does not clean up the R1 register on AVR
+/// If you are doing several 'scale8_video's in a row, use this, and
+/// then explicitly call cleanup_R1.
+LIB8STATIC void nscale8_video_LEAVING_R1_DIRTY( uint8_t & i, fract8 scale)
+{
+#if SCALE8_C == 1 || defined(LIB8_ATTINY)
+ i = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
+#elif SCALE8_AVRASM == 1
+ asm volatile(
+ " tst %[i]\n\t"
+ " breq L_%=\n\t"
+ " mul %[i], %[scale]\n\t"
+ " mov %[i], r1\n\t"
+ " breq L_%=\n\t"
+ " subi %[i], 0xFF\n\t"
+ "L_%=: \n\t"
+ : [i] "+a" (i)
+ : [scale] "a" (scale)
+ : "r0", "r1");
+#else
+#error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
+#endif
+}
+
+/// Clean up the r1 register after a series of *LEAVING_R1_DIRTY calls
+LIB8STATIC void cleanup_R1()
+{
+#if CLEANUP_R1_AVRASM == 1
+ // Restore r1 to "0"; it's expected to always be that
+ asm volatile( "clr __zero_reg__ \n\t" : : : "r1" );
+#endif
+}
+
+
+/// scale three one byte values by a fourth one, which is treated as
+/// the numerator of a fraction whose demominator is 256
+/// In other words, it computes r,g,b * (scale / 256)
+///
+/// THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENTS IN PLACE
+
+LIB8STATIC void nscale8x3( uint8_t& r, uint8_t& g, uint8_t& b, fract8 scale)
+{
+#if SCALE8_C == 1
+ r = ((int)r * (int)(scale) ) >> 8;
+ g = ((int)g * (int)(scale) ) >> 8;
+ b = ((int)b * (int)(scale) ) >> 8;
+#elif SCALE8_AVRASM == 1
+ r = scale8_LEAVING_R1_DIRTY(r, scale);
+ g = scale8_LEAVING_R1_DIRTY(g, scale);
+ b = scale8_LEAVING_R1_DIRTY(b, scale);
+ cleanup_R1();
+#else
+#error "No implementation for nscale8x3 available."
+#endif
+}
+
+/// scale three one byte values by a fourth one, which is treated as
+/// the numerator of a fraction whose demominator is 256
+/// In other words, it computes r,g,b * (scale / 256), ensuring
+/// that non-zero values passed in remain non zero, no matter how low the scale
+/// argument.
+///
+/// THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENTS IN PLACE
+LIB8STATIC void nscale8x3_video( uint8_t& r, uint8_t& g, uint8_t& b, fract8 scale)
+{
+#if SCALE8_C == 1
+ uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+ r = (r == 0) ? 0 : (((int)r * (int)(scale) ) >> 8) + nonzeroscale;
+ g = (g == 0) ? 0 : (((int)g * (int)(scale) ) >> 8) + nonzeroscale;
+ b = (b == 0) ? 0 : (((int)b * (int)(scale) ) >> 8) + nonzeroscale;
+#elif SCALE8_AVRASM == 1
+ nscale8_video_LEAVING_R1_DIRTY( r, scale);
+ nscale8_video_LEAVING_R1_DIRTY( g, scale);
+ nscale8_video_LEAVING_R1_DIRTY( b, scale);
+ cleanup_R1();
+#else
+#error "No implementation for nscale8x3 available."
+#endif
+}
+
+/// scale two one byte values by a third one, which is treated as
+/// the numerator of a fraction whose demominator is 256
+/// In other words, it computes i,j * (scale / 256)
+///
+/// THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENTS IN PLACE
+
+LIB8STATIC void nscale8x2( uint8_t& i, uint8_t& j, fract8 scale)
+{
+#if SCALE8_C == 1
+ i = ((int)i * (int)(scale) ) >> 8;
+ j = ((int)j * (int)(scale) ) >> 8;
+#elif SCALE8_AVRASM == 1
+ i = scale8_LEAVING_R1_DIRTY(i, scale);
+ j = scale8_LEAVING_R1_DIRTY(j, scale);
+ cleanup_R1();
+#else
+#error "No implementation for nscale8x2 available."
+#endif
+}
+
+/// scale two one byte values by a third one, which is treated as
+/// the numerator of a fraction whose demominator is 256
+/// In other words, it computes i,j * (scale / 256), ensuring
+/// that non-zero values passed in remain non zero, no matter how low the scale
+/// argument.
+///
+/// THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENTS IN PLACE
+
+
+LIB8STATIC void nscale8x2_video( uint8_t& i, uint8_t& j, fract8 scale)
+{
+#if SCALE8_C == 1
+ uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+ i = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
+ j = (j == 0) ? 0 : (((int)j * (int)(scale) ) >> 8) + nonzeroscale;
+#elif SCALE8_AVRASM == 1
+ nscale8_video_LEAVING_R1_DIRTY( i, scale);
+ nscale8_video_LEAVING_R1_DIRTY( j, scale);
+ cleanup_R1();
+#else
+#error "No implementation for nscale8x2 available."
+#endif
+}
+
+
+/// scale a 16-bit unsigned value by an 8-bit value,
+/// considered as numerator of a fraction whose denominator
+/// is 256. In other words, it computes i * (scale / 256)
+
+LIB8STATIC uint16_t scale16by8( uint16_t i, fract8 scale )
+{
+#if SCALE16BY8_C == 1
+ uint16_t result;
+ result = (i * scale) / 256;
+ return result;
+#elif SCALE16BY8_AVRASM == 1
+ uint16_t result = 0;
+ asm volatile(
+ // result.A = HighByte(i.A x j )
+ " mul %A[i], %[scale] \n\t"
+ " mov %A[result], r1 \n\t"
+ //" clr %B[result] \n\t"
+
+ // result.A-B += i.B x j
+ " mul %B[i], %[scale] \n\t"
+ " add %A[result], r0 \n\t"
+ " adc %B[result], r1 \n\t"
+
+ // cleanup r1
+ " clr __zero_reg__ \n\t"
+
+ : [result] "+r" (result)
+ : [i] "r" (i), [scale] "r" (scale)
+ : "r0", "r1"
+ );
+ return result;
+#else
+ #error "No implementation for scale16by8 available."
+#endif
+}
+
+/// scale a 16-bit unsigned value by a 16-bit value,
+/// considered as numerator of a fraction whose denominator
+/// is 65536. In other words, it computes i * (scale / 65536)
+
+LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale )
+{
+ #if SCALE16_C == 1
+ uint16_t result;
+ result = ((uint32_t)(i) * (uint32_t)(scale)) / 65536;
+ return result;
+#elif SCALE16_AVRASM == 1
+ uint32_t result = 0;
+ const uint8_t zero = 0;
+ asm volatile(
+ // result.A-B = i.A x scale.A
+ " mul %A[i], %A[scale] \n\t"
+ // save results...
+ // basic idea:
+ //" mov %A[result], r0 \n\t"
+ //" mov %B[result], r1 \n\t"
+ // which can be written as...
+ " movw %A[result], r0 \n\t"
+ // We actually need to do anything with r0,
+ // as result.A is never used again here, so we
+ // could just move the high byte, but movw is
+ // one clock cycle, just like mov, so might as
+ // well, in case we want to use this code for
+ // a generic 16x16 multiply somewhere.
+
+ // result.C-D = i.B x scale.B
+ " mul %B[i], %B[scale] \n\t"
+ //" mov %C[result], r0 \n\t"
+ //" mov %D[result], r1 \n\t"
+ " movw %C[result], r0 \n\t"
+
+ // result.B-D += i.B x scale.A
+ " mul %B[i], %A[scale] \n\t"
+
+ " add %B[result], r0 \n\t"
+ " adc %C[result], r1 \n\t"
+ " adc %D[result], %[zero] \n\t"
+
+ // result.B-D += i.A x scale.B
+ " mul %A[i], %B[scale] \n\t"
+
+ " add %B[result], r0 \n\t"
+ " adc %C[result], r1 \n\t"
+ " adc %D[result], %[zero] \n\t"
+
+ // cleanup r1
+ " clr r1 \n\t"
+
+ : [result] "+r" (result)
+ : [i] "r" (i),
+ [scale] "r" (scale),
+ [zero] "r" (zero)
+ : "r0", "r1"
+ );
+ result = result >> 16;
+ return result;
+#else
+ #error "No implementation for scale16 available."
+#endif
+}
+///@}
+
+///@defgroup Dimming Dimming and brightening functions
+///
+/// Dimming and brightening functions
+///
+/// The eye does not respond in a linear way to light.
+/// High speed PWM'd LEDs at 50% duty cycle appear far
+/// brighter then the 'half as bright' you might expect.
+///
+/// If you want your midpoint brightness leve (128) to
+/// appear half as bright as 'full' brightness (255), you
+/// have to apply a 'dimming function'.
+///@{
+
+/// Adjust a scaling value for dimming
+LIB8STATIC uint8_t dim8_raw( uint8_t x)
+{
+ return scale8( x, x);
+}
+
+/// Adjust a scaling value for dimming for video (value will never go below 1)
+LIB8STATIC uint8_t dim8_video( uint8_t x)
+{
+ return scale8_video( x, x);
+}
+
+/// Linear version of the dimming function that halves for values < 128
+LIB8STATIC uint8_t dim8_lin( uint8_t x )
+{
+ if( x & 0x80 ) {
+ x = scale8( x, x);
+ } else {
+ x += 1;
+ x /= 2;
+ }
+ return x;
+}
+
+/// inverse of the dimming function, brighten a value
+LIB8STATIC uint8_t brighten8_raw( uint8_t x)
+{
+ uint8_t ix = 255 - x;
+ return 255 - scale8( ix, ix);
+}
+
+/// inverse of the dimming function, brighten a value
+LIB8STATIC uint8_t brighten8_video( uint8_t x)
+{
+ uint8_t ix = 255 - x;
+ return 255 - scale8_video( ix, ix);
+}
+
+/// inverse of the dimming function, brighten a value
+LIB8STATIC uint8_t brighten8_lin( uint8_t x )
+{
+ uint8_t ix = 255 - x;
+ if( ix & 0x80 ) {
+ ix = scale8( ix, ix);
+ } else {
+ ix += 1;
+ ix /= 2;
+ }
+ return 255 - ix;
+}
+
+///@}
+#endif
diff --git a/lib8tion/trig8.h b/lib8tion/trig8.h
new file mode 100644
index 00000000..c41a0acc
--- /dev/null
+++ b/lib8tion/trig8.h
@@ -0,0 +1,259 @@
+#ifndef __INC_LIB8TION_TRIG_H
+#define __INC_LIB8TION_TRIG_H
+
+///@ingroup lib8tion
+
+///@defgroup Trig Fast trig functions
+/// Fast 8 and 16-bit approximations of sin(x) and cos(x).
+/// Don't use these approximations for calculating the
+/// trajectory of a rocket to Mars, but they're great
+/// for art projects and LED displays.
+///
+/// On Arduino/AVR, the 16-bit approximation is more than
+/// 10X faster than floating point sin(x) and cos(x), while
+/// the 8-bit approximation is more than 20X faster.
+///@{
+
+#if defined(__AVR__)
+#define sin16 sin16_avr
+#else
+#define sin16 sin16_C
+#endif
+
+/// Fast 16-bit approximation of sin(x). This approximation never varies more than
+/// 0.69% from the floating point value you'd get by doing
+///
+/// float s = sin(x) * 32767.0;
+///
+/// @param theta input angle from 0-65535
+/// @returns sin of theta, value between -32767 to 32767.
+LIB8STATIC int16_t sin16_avr( uint16_t theta )
+{
+ static const uint8_t data[] =
+ { 0, 0, 49, 0, 6393%256, 6393/256, 48, 0,
+ 12539%256, 12539/256, 44, 0, 18204%256, 18204/256, 38, 0,
+ 23170%256, 23170/256, 31, 0, 27245%256, 27245/256, 23, 0,
+ 30273%256, 30273/256, 14, 0, 32137%256, 32137/256, 4 /*,0*/ };
+
+ uint16_t offset = (theta & 0x3FFF);
+
+ // AVR doesn't have a multi-bit shift instruction,
+ // so if we say "offset >>= 3", gcc makes a tiny loop.
+ // Inserting empty volatile statements between each
+ // bit shift forces gcc to unroll the loop.
+ offset >>= 1; // 0..8191
+ asm volatile("");
+ offset >>= 1; // 0..4095
+ asm volatile("");
+ offset >>= 1; // 0..2047
+
+ if( theta & 0x4000 ) offset = 2047 - offset;
+
+ uint8_t sectionX4;
+ sectionX4 = offset / 256;
+ sectionX4 *= 4;
+
+ uint8_t m;
+
+ union {
+ uint16_t b;
+ struct {
+ uint8_t blo;
+ uint8_t bhi;
+ };
+ } u;
+
+ //in effect u.b = blo + (256 * bhi);
+ u.blo = data[ sectionX4 ];
+ u.bhi = data[ sectionX4 + 1];
+ m = data[ sectionX4 + 2];
+
+ uint8_t secoffset8 = (uint8_t)(offset) / 2;
+
+ uint16_t mx = m * secoffset8;
+
+ int16_t y = mx + u.b;
+ if( theta & 0x8000 ) y = -y;
+
+ return y;
+}
+
+/// Fast 16-bit approximation of sin(x). This approximation never varies more than
+/// 0.69% from the floating point value you'd get by doing
+///
+/// float s = sin(x) * 32767.0;
+///
+/// @param theta input angle from 0-65535
+/// @returns sin of theta, value between -32767 to 32767.
+LIB8STATIC int16_t sin16_C( uint16_t theta )
+{
+ static const uint16_t base[] =
+ { 0, 6393, 12539, 18204, 23170, 27245, 30273, 32137 };
+ static const uint8_t slope[] =
+ { 49, 48, 44, 38, 31, 23, 14, 4 };
+
+ uint16_t offset = (theta & 0x3FFF) >> 3; // 0..2047
+ if( theta & 0x4000 ) offset = 2047 - offset;
+
+ uint8_t section = offset / 256; // 0..7
+ uint16_t b = base[section];
+ uint8_t m = slope[section];
+
+ uint8_t secoffset8 = (uint8_t)(offset) / 2;
+
+ uint16_t mx = m * secoffset8;
+ int16_t y = mx + b;
+
+ if( theta & 0x8000 ) y = -y;
+
+ return y;
+}
+
+
+/// Fast 16-bit approximation of cos(x). This approximation never varies more than
+/// 0.69% from the floating point value you'd get by doing
+///
+/// float s = cos(x) * 32767.0;
+///
+/// @param theta input angle from 0-65535
+/// @returns sin of theta, value between -32767 to 32767.
+LIB8STATIC int16_t cos16( uint16_t theta)
+{
+ return sin16( theta + 16384);
+}
+
+///////////////////////////////////////////////////////////////////////
+
+// sin8 & cos8
+// Fast 8-bit approximations of sin(x) & cos(x).
+// Input angle is an unsigned int from 0-255.
+// Output is an unsigned int from 0 to 255.
+//
+// This approximation can vary to to 2%
+// from the floating point value you'd get by doing
+// float s = (sin( x ) * 128.0) + 128;
+//
+// Don't use this approximation for calculating the
+// "real" trigonometric calculations, but it's great
+// for art projects and LED displays.
+//
+// On Arduino/AVR, this approximation is more than
+// 20X faster than floating point sin(x) and cos(x)
+
+#if defined(__AVR__) && !defined(LIB8_ATTINY)
+#define sin8 sin8_avr
+#else
+#define sin8 sin8_C
+#endif
+
+
+const uint8_t b_m16_interleave[] = { 0, 49, 49, 41, 90, 27, 117, 10 };
+
+/// Fast 8-bit approximation of sin(x). This approximation never varies more than
+/// 2% from the floating point value you'd get by doing
+///
+/// float s = (sin(x) * 128.0) + 128;
+///
+/// @param theta input angle from 0-255
+/// @returns sin of theta, value between 0 and 255
+LIB8STATIC uint8_t sin8_avr( uint8_t theta)
+{
+ uint8_t offset = theta;
+
+ asm volatile(
+ "sbrc %[theta],6 \n\t"
+ "com %[offset] \n\t"
+ : [theta] "+r" (theta), [offset] "+r" (offset)
+ );
+
+ offset &= 0x3F; // 0..63
+
+ uint8_t secoffset = offset & 0x0F; // 0..15
+ if( theta & 0x40) secoffset++;
+
+ uint8_t m16; uint8_t b;
+
+ uint8_t section = offset >> 4; // 0..3
+ uint8_t s2 = section * 2;
+
+ const uint8_t* p = b_m16_interleave;
+ p += s2;
+ b = *p;
+ p++;
+ m16 = *p;
+
+ uint8_t mx;
+ uint8_t xr1;
+ asm volatile(
+ "mul %[m16],%[secoffset] \n\t"
+ "mov %[mx],r0 \n\t"
+ "mov %[xr1],r1 \n\t"
+ "eor r1, r1 \n\t"
+ "swap %[mx] \n\t"
+ "andi %[mx],0x0F \n\t"
+ "swap %[xr1] \n\t"
+ "andi %[xr1], 0xF0 \n\t"
+ "or %[mx], %[xr1] \n\t"
+ : [mx] "=r" (mx), [xr1] "=r" (xr1)
+ : [m16] "r" (m16), [secoffset] "r" (secoffset)
+ );
+
+ int8_t y = mx + b;
+ if( theta & 0x80 ) y = -y;
+
+ y += 128;
+
+ return y;
+}
+
+
+/// Fast 8-bit approximation of sin(x). This approximation never varies more than
+/// 2% from the floating point value you'd get by doing
+///
+/// float s = (sin(x) * 128.0) + 128;
+///
+/// @param theta input angle from 0-255
+/// @returns sin of theta, value between 0 and 255
+LIB8STATIC uint8_t sin8_C( uint8_t theta)
+{
+ uint8_t offset = theta;
+ if( theta & 0x40 ) {
+ offset = (uint8_t)255 - offset;
+ }
+ offset &= 0x3F; // 0..63
+
+ uint8_t secoffset = offset & 0x0F; // 0..15
+ if( theta & 0x40) secoffset++;
+
+ uint8_t section = offset >> 4; // 0..3
+ uint8_t s2 = section * 2;
+ const uint8_t* p = b_m16_interleave;
+ p += s2;
+ uint8_t b = *p;
+ p++;
+ uint8_t m16 = *p;
+
+ uint8_t mx = (m16 * secoffset) >> 4;
+
+ int8_t y = mx + b;
+ if( theta & 0x80 ) y = -y;
+
+ y += 128;
+
+ return y;
+}
+
+/// Fast 8-bit approximation of cos(x). This approximation never varies more than
+/// 2% from the floating point value you'd get by doing
+///
+/// float s = (cos(x) * 128.0) + 128;
+///
+/// @param theta input angle from 0-255
+/// @returns sin of theta, value between 0 and 255
+LIB8STATIC uint8_t cos8( uint8_t theta)
+{
+ return sin8( theta + 64);
+}
+
+///@}
+#endif
diff --git a/library.properties b/library.properties
new file mode 100644
index 00000000..f5093f33
--- /dev/null
+++ b/library.properties
@@ -0,0 +1,9 @@
+name=FastLED
+version=3.1.0
+author=Daniel Garcia
+maintainer=Daniel Garcia <dgarcia@fastled.io>
+sentence=Multi-platform library for controlling dozens of different types of LEDs along with optimized math, effect, and noise functions.
+paragraph=Multi-platform library for controlling dozens of different types of LEDs along with optimized math, effect, and noise functions.
+category=Display
+url=https://github.com/FastLED/FastLED
+architectures=*
diff --git a/noise.cpp b/noise.cpp
index f60accf7..190e74e7 100644
--- a/noise.cpp
+++ b/noise.cpp
@@ -1,25 +1,9 @@
-#include <FastLED.h>
+#define FASTLED_INTERNAL
+#include "FastLED.h"
-#ifdef FASTLED_AVR
-#include <avr/pgmspace.h>
-#define USE_PROGMEM
-#endif
+FASTLED_NAMESPACE_BEGIN
-// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734
-#ifdef FASTLED_AVR
-#ifdef PROGMEM
-#undef PROGMEM
-#define PROGMEM __attribute__((section(".progmem.data")))
-#endif
-#endif
-
-#ifdef USE_PROGMEM
-#define FL_PROGMEM PROGMEM
-#define P(x) pgm_read_byte_near(p + x)
-#else
-#define FL_PROGMEM
-#define P(x) p[(x)]
-#endif
+#define P(x) FL_PGM_READ_BYTE_NEAR(p + x)
FL_PROGMEM static uint8_t const p[] = { 151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
@@ -47,7 +31,7 @@ FL_PROGMEM static uint8_t const p[] = { 151,160,137,91,90,15,
#define FADE(x) scale16(x,x)
#define LERP(a,b,u) lerp15by16(a,b,u)
#endif
-static int16_t __attribute__((always_inline)) grad16(uint8_t hash, int16_t x, int16_t y, int16_t z) {
+static int16_t inline __attribute__((always_inline)) grad16(uint8_t hash, int16_t x, int16_t y, int16_t z) {
#if 0
switch(hash & 0xF) {
case 0: return (( x) + ( y))>>1;
@@ -78,7 +62,7 @@ static int16_t __attribute__((always_inline)) grad16(uint8_t hash, int16_t x, i
#endif
}
-static int16_t __attribute__((always_inline)) grad16(uint8_t hash, int16_t x, int16_t y) {
+static int16_t inline __attribute__((always_inline)) grad16(uint8_t hash, int16_t x, int16_t y) {
hash = hash & 7;
int16_t u,v;
if(hash < 4) { u = x; v = y; } else { u = y; v = x; }
@@ -88,7 +72,7 @@ static int16_t __attribute__((always_inline)) grad16(uint8_t hash, int16_t x, in
return (u+v)>>1;
}
-static int16_t __attribute__((always_inline)) grad16(uint8_t hash, int16_t x) {
+static int16_t inline __attribute__((always_inline)) grad16(uint8_t hash, int16_t x) {
hash = hash & 15;
int16_t u,v;
if(hash > 8) { u=x;v=x; }
@@ -100,7 +84,30 @@ static int16_t __attribute__((always_inline)) grad16(uint8_t hash, int16_t x) {
return (u+v)>>1;
}
-static int8_t __attribute__((always_inline)) grad8(uint8_t hash, int8_t x, int8_t y, int8_t z) {
+// selectBasedOnHashBit performs this:
+// result = (hash & (1<<bitnumber)) ? a : b
+// but with an AVR asm version that's smaller and quicker than C
+// (and probably not worth including in lib8tion)
+static int8_t inline __attribute__((always_inline)) selectBasedOnHashBit(uint8_t hash, uint8_t bitnumber, int8_t a, int8_t b) {
+ int8_t result;
+#if !defined(__AVR__)
+ result = (hash & (1<<bitnumber)) ? a : b;
+#else
+ asm volatile(
+ "mov %[result],%[a] \n\t"
+ "sbrs %[hash],%[bitnumber] \n\t"
+ "mov %[result],%[b] \n\t"
+ : [result] "=r" (result)
+ : [hash] "r" (hash),
+ [bitnumber] "M" (bitnumber),
+ [a] "r" (a),
+ [b] "r" (b)
+ );
+#endif
+ return result;
+}
+
+static int8_t inline __attribute__((always_inline)) grad8(uint8_t hash, int8_t x, int8_t y, int8_t z) {
#if 0
switch(hash & 0xF) {
case 0: return (( x) + ( y))>>1;
@@ -121,36 +128,75 @@ static int8_t __attribute__((always_inline)) grad8(uint8_t hash, int8_t x, int8
case 15: return ((-y) + (-z))>>1;
}
#else
+
hash &= 0xF;
- int8_t u = (hash&8)?y:x;
- int8_t v = hash<4?y:hash==12||hash==14?x:z;
+
+ int8_t u, v;
+ //u = (hash&8)?y:x;
+ u = selectBasedOnHashBit( hash, 3, y, x);
+
+#if 1
+ v = hash<4?y:hash==12||hash==14?x:z;
+#else
+ // Verbose version for analysis; generates idenitical code.
+ if( hash < 4) { // 00 01 02 03
+ v = y;
+ } else {
+ if( hash==12 || hash==14) { // 0C 0E
+ v = x;
+ } else {
+ v = z; // 04 05 06 07 08 09 0A 0B 0D 0F
+ }
+ }
+#endif
+
if(hash&1) { u = -u; }
if(hash&2) { v = -v; }
- return (u+v)>>1;
+ return avg7(u,v);
#endif
}
-static int8_t __attribute__((always_inline)) grad8(uint8_t hash, int8_t x, int8_t y) {
- hash = hash & 7;
+static int8_t inline __attribute__((always_inline)) grad8(uint8_t hash, int8_t x, int8_t y)
+{
+ // since the tests below can be done bit-wise on the bottom
+ // three bits, there's no need to mask off the higher bits
+ // hash = hash & 7;
+
int8_t u,v;
- if(hash < 4) { u = x; v = y; } else { u = y; v = x; }
+ if( hash & 4) {
+ u = y; v = x;
+ } else {
+ u = x; v = y;
+ }
+
if(hash&1) { u = -u; }
if(hash&2) { v = -v; }
- return (u+v)>>1;
+ return avg7(u,v);
}
-static int8_t __attribute__((always_inline)) grad8(uint8_t hash, int8_t x) {
- hash = hash & 15;
+static int8_t inline __attribute__((always_inline)) grad8(uint8_t hash, int8_t x)
+{
+ // since the tests below can be done bit-wise on the bottom
+ // four bits, there's no need to mask off the higher bits
+ // hash = hash & 15;
+
int8_t u,v;
- if(hash > 8) { u=x;v=x; }
- else if(hash < 4) { u=x;v=1; }
- else { u=1;v=x; }
+ if(hash & 8) {
+ u=x; v=x;
+ } else {
+ if(hash & 4) {
+ u=1; v=x;
+ } else {
+ u=x; v=1;
+ }
+ }
+
if(hash&1) { u = -u; }
if(hash&2) { v = -v; }
- return (u+v)>>1;
+ return avg7(u,v);
}
@@ -159,7 +205,7 @@ uint16_t logfade12(uint16_t val) {
return scale16(val,val)>>4;
}
-static int16_t __attribute__((always_inline)) lerp15by12( int16_t a, int16_t b, fract16 frac)
+static int16_t inline __attribute__((always_inline)) lerp15by12( int16_t a, int16_t b, fract16 frac)
{
//if(1) return (lerp(frac,a,b));
int16_t result;
@@ -176,7 +222,7 @@ static int16_t __attribute__((always_inline)) lerp15by12( int16_t a, int16_t b,
}
#endif
-static int8_t __attribute__((always_inline)) lerp7by8( int8_t a, int8_t b, fract8 frac)
+static int8_t inline __attribute__((always_inline)) lerp7by8( int8_t a, int8_t b, fract8 frac)
{
// int8_t delta = b - a;
// int16_t prod = (uint16_t)delta * (uint16_t)frac;
@@ -342,9 +388,9 @@ int8_t inoise8_raw(uint16_t x, uint16_t y, uint16_t z)
uint8_t w = z;
// Get a signed version of the above for the grad function
- int8_t xx = (x>>1) & 0x7F;
- int8_t yy = (y>>1) & 0x7F;
- int8_t zz = (z>>1) & 0x7F;
+ int8_t xx = ((uint8_t)(x)>>1) & 0x7F;
+ int8_t yy = ((uint8_t)(y)>>1) & 0x7F;
+ int8_t zz = ((uint8_t)(z)>>1) & 0x7F;
uint8_t N = 0x80;
// u = FADE(u); v = FADE(v); w = FADE(w);
@@ -386,8 +432,8 @@ int8_t inoise8_raw(uint16_t x, uint16_t y)
uint8_t v = y;
// Get a signed version of the above for the grad function
- int8_t xx = (x>>1) & 0x7F;
- int8_t yy = (y>>1) & 0x7F;
+ int8_t xx = ((uint8_t)(x)>>1) & 0x7F;
+ int8_t yy = ((uint8_t)(y)>>1) & 0x7F;
uint8_t N = 0x80;
// u = FADE(u); v = FADE(v); w = FADE(w);
@@ -421,7 +467,7 @@ int8_t inoise8_raw(uint16_t x)
uint8_t u = x;
// Get a signed version of the above for the grad function
- int8_t xx = (x>>1) & 0x7F;
+ int8_t xx = ((uint8_t)(x)>>1) & 0x7F;
uint8_t N = 0x80;
u = scale8(u,u);
@@ -514,12 +560,12 @@ void fill_raw_2dnoise8(uint8_t *pData, int width, int height, uint8_t octaves, q
}
void fill_raw_2dnoise8(uint8_t *pData, int width, int height, uint8_t octaves, uint16_t x, int scalex, uint16_t y, int scaley, uint16_t time) {
- fill_raw_2dnoise8(pData, width, height, octaves, q44(2,0), 171, 1, x, scalex, y, scaley, time);
+ fill_raw_2dnoise8(pData, width, height, octaves, q44(2,0), 128, 1, x, scalex, y, scaley, time);
}
void fill_raw_2dnoise16(uint16_t *pData, int width, int height, uint8_t octaves, q88 freq88, fract16 amplitude, int skip, uint32_t x, int scalex, uint32_t y, int scaley, uint32_t time) {
if(octaves > 1) {
- fill_raw_2dnoise16(pData, width, height, octaves-1, freq88, amplitude, skip+1, x *freq88 , scalex *freq88, y * freq88, scaley * freq88, time);
+ fill_raw_2dnoise16(pData, width, height, octaves-1, freq88, amplitude, skip, x *freq88 , scalex *freq88, y * freq88, scaley * freq88, time);
} else {
// amplitude is always 255 on the lowest level
amplitude=65535;
@@ -694,3 +740,5 @@ void fill_2dnoise16(CRGB *leds, int width, int height, bool serpentine,
}
}
}
+
+FASTLED_NAMESPACE_END
diff --git a/noise.h b/noise.h
index 4e11a318..b968815a 100644
--- a/noise.h
+++ b/noise.h
@@ -1,103 +1,79 @@
#ifndef __INC_NOISE_H
#define __INC_NOISE_H
-#if 0
-/// Class for accessing 16 bit noise. Provides methods for setting origin, scale,
-/// frequency, alplitude, time, octave blurring
-class CFastNoise16 {
-public:
- CFastNoise16 &setOrigin(uint32_t x, uint32_t y, uint32_t z);
- CFastNoise16 &setOrigin(uint32_t x, uint32_t y);
- CFastNoise16 &setOrigin(uint32_t x);
+FASTLED_NAMESPACE_BEGIN
- uint32_t getX();
- uint32_t getY();
- uint32_t getZ();
- uint32_t getTime();
+///@file noise.h
+/// Noise functions provided by the library.
- uint32_t getOrigin(uint32_t & x, uint32_t & y, uint32_t & z);
- uint32_t getOrigin(uint32_t & x, uint32_t & y);
- uint32_t getOrigin(uint32_t & x);
+///@defgroup Noise Noise functions
+///Simplex noise function definitions
+///@{
+/// @name scaled 16 bit noise functions
+///@{
+/// 16 bit, fixed point implementation of perlin's Simplex Noise. Coordinates are
+/// 16.16 fixed point values, 32 bit integers with integral coordinates in the high 16
+/// bits and fractional in the low 16 bits, and the function takes 1d, 2d, and 3d coordinate
+/// values. These functions are scaled to return 0-65535
- CFastNoise16 &advance(int32_t x, int32_t y, int32_t z);
- CFastNoise16 &advance(int32_t x, int32_t y;
- CFastNoise16 &advance(int32_t x);
-
- CFastNoise16 &advanceTime(int32_t t);
-
- CFastNoise16 &setScale(int32_t x_scale, int32_t y_scale, int32_t z_scale);
- CFastNoise16 &setScale(int32_t x_scale, int32_t y_scale);
- CFastNoise16 &setScale(int32_t x_scale);
-
- int32_t getScaleX();
- int32_t getScaleY();
- int32_t getScaleZ();
- void getScale(int32_t & x, int32_t & y, int32_t & z);
- void getScale(int32_t & x, int32_t & y);
- void getScale(int32_t & x);
-
- CFastNoise16 &setAmplitude(fract16 amplitude);
-
- CFastNoise16 &setFrequency(q88 frequency);
- CFastNoise16 &setTime(uint32_t time);
-
- CFastNoise16 &setOctaves(int octaves);
-
- CFastNoise16 &setOctaveBlur(bool blurOctaves);
-
- void getNoise(uint32_t x, uint32_t y, uint32_t z);
- void getNoise(uint32_t x, uint32_t y);
- void getNoise(uint32_t x);
-
- void fillNoise(uint16_t *pData, int size);
- void fillNoise(uint16_t *pData, int width, int height);
- void fillNoise(uint16_t *pData, int width, int height, int depth);
-
- void fillNoise(uint8_t *pData, int size);
- void fillNoise(uint8_t *pData, int width, int height);
- void fillNoise(uint8_t *pData, int width, int height, int depth);
-};
-#endif
-
-// 16 bit, fixed point implementation of perlin's Simplex Noise. Coordinates are
-// 16.16 fixed point values, 32 bit integers with integral coordinates in the high 16
-// bits and fractional in the low 16 bits, and the function takes 1d, 2d, and 3d coordinate
-// values. These functions are scaled to return 0-65535
extern uint16_t inoise16(uint32_t x, uint32_t y, uint32_t z);
extern uint16_t inoise16(uint32_t x, uint32_t y);
extern uint16_t inoise16(uint32_t x);
+///@}
-// 16 bit raw versions of the noise functions. These values are not scaled/altered and have
-// output values roughly in the range (-18k,18k)
+/// @name raw 16 bit noise functions
+//@{
+/// 16 bit raw versions of the noise functions. These values are not scaled/altered and have
+/// output values roughly in the range (-18k,18k)
extern int16_t inoise16_raw(uint32_t x, uint32_t y, uint32_t z);
extern int16_t inoise16_raw(uint32_t x, uint32_t y);
extern int16_t inoise16_raw(uint32_t x);
-
-// 8 bit, fixed point implementation of perlin's Simplex Noise. Coordinates are
-// 8.8 fixed point values, 16 bit integers with integral coordinates in the high 8
-// bits and fractional in the low 8 bits, and the function takes 1d, 2d, and 3d coordinate
-// values. These functions are scaled to return 0-255
+///@}
+
+/// @name 8 bit scaled noise functions
+///@{
+/// 8 bit, fixed point implementation of perlin's Simplex Noise. Coordinates are
+/// 8.8 fixed point values, 16 bit integers with integral coordinates in the high 8
+/// bits and fractional in the low 8 bits, and the function takes 1d, 2d, and 3d coordinate
+/// values. These functions are scaled to return 0-255
extern uint8_t inoise8(uint16_t x, uint16_t y, uint16_t z);
extern uint8_t inoise8(uint16_t x, uint16_t y);
extern uint8_t inoise8(uint16_t x);
+///@}
-// 8 bit raw versions of the noise functions. These values are not scaled/altered and have
-// output values roughly in the range (-70,70)
+/// @name 8 bit raw noise functions
+///@{
+/// 8 bit raw versions of the noise functions. These values are not scaled/altered and have
+/// output values roughly in the range (-70,70)
extern int8_t inoise8_raw(uint16_t x, uint16_t y, uint16_t z);
extern int8_t inoise8_raw(uint16_t x, uint16_t y);
extern int8_t inoise8_raw(uint16_t x);
-
-// Raw noise fill functions - fill into a 1d or 2d array of 8-bit values using either 8-bit noise or 16-bit noise
-// functions.
-void fill_raw_noise8(uint8_t *pData, uint8_t num_points, uint8_t octaves, uint16_t x, int scale, uint16_t time);
-void fill_raw_noise16into8(uint8_t *pData, uint8_t num_points, uint8_t octaves, uint32_t x, int scale, uint32_t time);
+///@}
+
+///@name raw fill functions
+///@{
+/// Raw noise fill functions - fill into a 1d or 2d array of 8-bit values using either 8-bit noise or 16-bit noise
+/// functions.
+///@param pData the array of data to write into
+///@param num_points the number of points of noise to compute
+///@param octaves the number of octaves to use for noise
+///@param x the x position in the noise field
+///@param y the y position in the noise field for 2d functions
+///@param scalex the scale (distance) between x points when filling in noise
+///@param scaley the scale (distance) between y points when filling in noise
+///@param time the time position for the noise field
+void fill_raw_noise8(uint8_t *pData, uint8_t num_points, uint8_t octaves, uint16_t x, int scalex, uint16_t time);
+void fill_raw_noise16into8(uint8_t *pData, uint8_t num_points, uint8_t octaves, uint32_t x, int scalex, uint32_t time);
void fill_raw_2dnoise8(uint8_t *pData, int width, int height, uint8_t octaves, uint16_t x, int scalex, uint16_t y, int scaley, uint16_t time);
void fill_raw_2dnoise16into8(uint8_t *pData, int width, int height, uint8_t octaves, uint32_t x, int scalex, uint32_t y, int scaley, uint32_t time);
void fill_raw_2dnoise16(uint16_t *pData, int width, int height, uint8_t octaves, q88 freq88, fract16 amplitude, int skip, uint32_t x, int scalex, uint32_t y, int scaley, uint32_t time);
void fill_raw_2dnoise16into8(uint8_t *pData, int width, int height, uint8_t octaves, q44 freq44, fract8 amplitude, int skip, uint32_t x, int scalex, uint32_t y, int scaley, uint32_t time);
+///@}
-// fill functions to fill leds with values based on noise functions. These functions use the fill_raw_* functions as appropriate.
+///@name fill functions
+///@{
+/// fill functions to fill leds with values based on noise functions. These functions use the fill_raw_* functions as appropriate.
void fill_noise8(CRGB *leds, int num_leds,
uint8_t octaves, uint16_t x, int scale,
uint8_t hue_octaves, uint16_t hue_x, int hue_scale,
@@ -113,4 +89,7 @@ void fill_2dnoise16(CRGB *leds, int width, int height, bool serpentine,
uint8_t octaves, uint32_t x, int xscale, uint32_t y, int yscale, uint32_t time,
uint8_t hue_octaves, uint16_t hue_x, int hue_xscale, uint16_t hue_y, uint16_t hue_yscale,uint16_t hue_time, bool blend, uint16_t hue_shift=0);
+FASTLED_NAMESPACE_END
+///@}
+
#endif
diff --git a/pixeltypes.h b/pixeltypes.h
index e112636b..2ad2c78b 100644
--- a/pixeltypes.h
+++ b/pixeltypes.h
@@ -5,14 +5,19 @@
#include "lib8tion.h"
#include "color.h"
+FASTLED_NAMESPACE_BEGIN
+
struct CRGB;
struct CHSV;
-// Forward declaration of hsv2rgb_rainbow here,
-// to avoid circular dependencies.
-extern void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb);
+///@defgroup Pixeltypes CHSV and CRGB type definitions
+///@{
+/// Forward declaration of hsv2rgb_rainbow here,
+/// to avoid circular dependencies.
+extern void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb);
+/// Representation of an HSV pixel (hue, saturation, value (aka brightness)).
struct CHSV {
union {
struct {
@@ -67,6 +72,7 @@ struct CHSV {
}
};
+/// Pre-defined hue values for HSV objects
typedef enum {
HUE_RED = 0,
HUE_ORANGE = 32,
@@ -78,6 +84,7 @@ typedef enum {
HUE_PINK = 224
} HSVHue;
+/// Representation of an RGB pixel (Red, Green, Blue)
struct CRGB {
union {
struct {
@@ -343,6 +350,26 @@ struct CRGB {
return *this;
}
+ // scale down a RGB to N 256ths of it's current brightness, using
+ // 'plain math' dimming rules, which means that if the low light levels
+ // may dim all the way to 100% black.
+ inline CRGB& nscale8 (const CRGB & scaledown )
+ {
+ r = ::scale8(r, scaledown.r);
+ g = ::scale8(g, scaledown.g);
+ b = ::scale8(b, scaledown.b);
+ return *this;
+ }
+
+ inline CRGB scale8 (const CRGB & scaledown )
+ {
+ CRGB out;
+ out.r = ::scale8(r, scaledown.r);
+ out.g = ::scale8(g, scaledown.g);
+ out.b = ::scale8(b, scaledown.b);
+ return out;
+ }
+
// fadeToBlackBy is a synonym for nscale8( ..., 255-fadefactor)
inline CRGB& fadeToBlackBy (uint8_t fadefactor )
{
@@ -398,7 +425,7 @@ struct CRGB {
return retval;
}
-#ifdef SmartMatrix_h
+#if (defined SmartMatrix_h || defined SmartMatrix3_h)
operator rgb24() const {
rgb24 ret;
ret.red = r;
@@ -408,7 +435,7 @@ struct CRGB {
}
#endif
- inline uint8_t getLuma ( ) {
+ inline uint8_t getLuma ( ) const {
//Y' = 0.2126 R' + 0.7152 G' + 0.0722 B'
// 54 183 18 (!)
@@ -419,7 +446,7 @@ struct CRGB {
return luma;
}
- inline uint8_t getAverageLight( ) {
+ inline uint8_t getAverageLight( ) const {
const uint8_t eightysix = 86;
uint8_t avg = scale8_LEAVING_R1_DIRTY( r, eightysix) + \
scale8_LEAVING_R1_DIRTY( g, eightysix) + \
@@ -460,6 +487,85 @@ struct CRGB {
return ret;
}
+ // getParity returns 0 or 1, depending on the
+ // lowest bit of the sum of the color components.
+ inline uint8_t getParity()
+ {
+ uint8_t sum = r + g + b;
+ return (sum & 0x01);
+ }
+
+ // setParity adjusts the color in the smallest
+ // way possible so that the parity of the color
+ // is now the desired value. This allows you to
+ // 'hide' one bit of information in the color.
+ //
+ // Ideally, we find one color channel which already
+ // has data in it, and modify just that channel by one.
+ // We don't want to light up a channel that's black
+ // if we can avoid it, and if the pixel is 'grayscale',
+ // (meaning that R==G==B), we modify all three channels
+ // at once, to preserve the neutral hue.
+ //
+ // There's no such thing as a free lunch; in many cases
+ // this 'hidden bit' may actually be visible, but this
+ // code makes reasonable efforts to hide it as much
+ // as is reasonably possible.
+ //
+ // Also, an effort is made to have make it such that
+ // repeatedly setting the parity to different values
+ // will not cause the color to 'drift'. Toggling
+ // the parity twice should generally result in the
+ // original color again.
+ //
+ inline void setParity( uint8_t parity)
+ {
+ uint8_t curparity = getParity();
+
+ if( parity == curparity) return;
+
+ if( parity ) {
+ // going 'up'
+ if( (b > 0) && (b < 255)) {
+ if( r == g && g == b) {
+ r++;
+ g++;
+ }
+ b++;
+ } else if( (r > 0) && (r < 255)) {
+ r++;
+ } else if( (g > 0) && (g < 255)) {
+ g++;
+ } else {
+ if( r == g && g == b) {
+ r ^= 0x01;
+ g ^= 0x01;
+ }
+ b ^= 0x01;
+ }
+ } else {
+ // going 'down'
+ if( b > 1) {
+ if( r == g && g == b) {
+ r--;
+ g--;
+ }
+ b--;
+ } else if( g > 1) {
+ g--;
+ } else if( r > 1) {
+ r--;
+ } else {
+ if( r == g && g == b) {
+ r ^= 0x01;
+ g ^= 0x01;
+ }
+ b ^= 0x01;
+ }
+ }
+ }
+
+ /// Predefined RGB colors
typedef enum {
AliceBlue=0xF0F8FF,
Amethyst=0x9966CC,
@@ -608,9 +714,17 @@ struct CRGB {
White=0xFFFFFF,
WhiteSmoke=0xF5F5F5,
Yellow=0xFFFF00,
- YellowGreen=0x9ACD32
+ YellowGreen=0x9ACD32,
+
+ // LED RGB color that roughly approximates
+ // the color of incandescent fairy lights,
+ // assuming that you're using FastLED
+ // color correction on your LEDs (recommended).
+ FairyLight=0xFFE42D,
+ // If you are using no color correction, use this
+ FairyLightNCC=0xFF9D2A
+
} HTMLColorCode;
- // static uint32_t Squant;
};
@@ -714,7 +828,9 @@ inline CRGB operator%( const CRGB& p1, uint8_t d)
-// Define RGB orderings
+/// RGB orderings, used when instantiating controllers to determine what
+/// order the controller should send RGB data out in, RGB being the default
+/// ordering.
enum EOrder {
RGB=0012,
RBG=0021,
@@ -724,5 +840,7 @@ enum EOrder {
BGR=0210
};
+FASTLED_NAMESPACE_END
+///@}
#endif
diff --git a/platforms.h b/platforms.h
new file mode 100644
index 00000000..4f5d7ee6
--- /dev/null
+++ b/platforms.h
@@ -0,0 +1,28 @@
+#ifndef __INC_PLATFORMS_H
+#define __INC_PLATFORMS_H
+
+#include "fastled_config.h"
+
+#if defined(NRF51)
+#include "platforms/arm/nrf51/fastled_arm_nrf51.h"
+#elif defined(__MK20DX128__) || defined(__MK20DX256__)
+// Include k20/T3 headers
+#include "platforms/arm/k20/fastled_arm_k20.h"
+#elif defined(__MKL26Z64__)
+// Include kl26/T-LC headers
+#include "platforms/arm/kl26/fastled_arm_kl26.h"
+#elif defined(__SAM3X8E__)
+// Include sam/due headers
+#include "platforms/arm/sam/fastled_arm_sam.h"
+#elif defined(STM32F10X_MD)
+#include "platforms/arm/stm32/fastled_arm_stm32.h"
+#elif defined(__SAMD21G18A__)
+#include "platforms/arm/d21/fastled_arm_d21.h"
+#elif defined(__XTENSA__)
+#error "XTENSA-architecture microcontrollers are not supported"
+#else
+// AVR platforms
+#include "platforms/avr/fastled_avr.h"
+#endif
+
+#endif
diff --git a/platforms/arm/common/m0clockless.h b/platforms/arm/common/m0clockless.h
new file mode 100644
index 00000000..3d12ade4
--- /dev/null
+++ b/platforms/arm/common/m0clockless.h
@@ -0,0 +1,313 @@
+#ifndef __INC_M0_CLOCKLESS_H
+#define __INC_M0_CLOCKLESS_H
+
+struct M0ClocklessData {
+ uint8_t d[3];
+ uint8_t s[3];
+ uint8_t e[3];
+ uint8_t adj;
+};
+
+
+template<int HI_OFFSET, int LO_OFFSET, int T1, int T2, int T3, EOrder RGB_ORDER, int WAIT_TIME>int
+showLedData(volatile uint32_t *_port, uint32_t _bitmask, const uint8_t *_leds, uint32_t num_leds, struct M0ClocklessData *pData) {
+ // Lo register variables
+ register uint32_t scratch=0;
+ register struct M0ClocklessData *base = pData;
+ register volatile uint32_t *port = _port;
+ register uint32_t d=0;
+ register uint32_t counter=num_leds;
+ register uint32_t bn=0;
+ register uint32_t b=0;
+ register uint32_t bitmask = _bitmask;
+
+ // high register variable
+ register const uint8_t *leds = _leds;
+
+ asm __volatile__ (
+ ///////////////////////////////////////////////////////////////////////////
+ //
+ // asm macro definitions - used to assemble the clockless output
+ //
+ ".ifnotdef fl_delay_def;"
+#ifdef FASTLED_ARM_M0_PLUS
+ " .set fl_is_m0p, 1;"
+ " .macro m0pad;"
+ " nop;"
+ " .endm;"
+#else
+ " .set fl_is_m0p, 0;"
+ " .macro m0pad;"
+ " .endm;"
+#endif
+ " .set fl_delay_def, 1;"
+ " .set fl_delay_mod, 4;"
+ " .if fl_is_m0p == 1;"
+ " .set fl_delay_mod, 3;"
+ " .endif;"
+ " .macro fl_delay dtime, reg=r0;"
+ " .if (\\dtime > 0);"
+ " .set dcycle, (\\dtime / fl_delay_mod);"
+ " .set dwork, (dcycle * fl_delay_mod);"
+ " .set drem, (\\dtime - dwork);"
+ " .rept (drem);"
+ " nop;"
+ " .endr;"
+ " .if dcycle > 0;"
+ " mov \\reg, #dcycle;"
+ " delayloop_\\@:;"
+ " sub \\reg, #1;"
+ " bne delayloop_\\@;"
+ " .if fl_is_m0p == 0;"
+ " nop;"
+ " .endif;"
+ " .endif;"
+ " .endif;"
+ " .endm;"
+
+ " .macro mod_delay dtime,b1,b2,reg;"
+ " .set adj, (\\b1 + \\b2);"
+ " .if adj < \\dtime;"
+ " .set dtime2, (\\dtime - adj);"
+ " fl_delay dtime2, \\reg;"
+ " .endif;"
+ " .endm;"
+
+ // check the bit and drop the line low if it isn't set
+ " .macro qlo4 b,bitmask,port,loff ;"
+ " lsl \\b, #1 ;"
+ " bcs skip_\\@ ;"
+ " str \\bitmask, [\\port, \\loff] ;"
+ " skip_\\@: ;"
+ " m0pad;"
+ " .endm ;"
+
+ // set the pin hi or low (determined by the offset passed in )
+ " .macro qset2 bitmask,port,loff;"
+ " str \\bitmask, [\\port, \\loff];"
+ " m0pad;"
+ " .endm;"
+
+ // Load up the next led byte to work with, put it in bn
+ " .macro loadleds3 leds, bn, rled, scratch;"
+ " mov \\scratch, \\leds;"
+ " ldrb \\bn, [\\scratch, \\rled];"
+ " .endm;"
+
+ // check whether or not we should dither
+ " .macro loaddither7 bn,d,base,rdither;"
+ " ldrb \\d, [\\base, \\rdither];"
+ " lsl \\d, #24;" //; shift high for the qadd w/bn
+ " lsl \\bn, #24;" //; shift high for the qadd w/d
+ " bne chkskip_\\@;" //; if bn==0, clear d;"
+ " eor \\d, \\d;" //; clear d;"
+ " m0pad;"
+ " chkskip_\\@:;"
+ " .endm;"
+
+ // Do the qadd8 for dithering -- there's two versions of this. The m0 version
+ // takes advantage of the 3 cycle branch to do two things after the branch,
+ // while keeping timing constant. The m0+, however, branches in 2 cycles, so
+ // we have to work around that a bit more. This is one of the few times
+ // where the m0 will actually be _more_ efficient than the m0+
+ " .macro dither5 bn,d;"
+ " .syntax unified;"
+ " .if fl_is_m0p == 0;"
+ " adds \\bn, \\d;" // do the add
+ " bcc dither5_1_\\@;"
+ " mvns \\bn, \\bn;" // set the low 24bits ot 1's
+ " lsls \\bn, \\bn, #24;" // move low 8 bits to the high bits
+ " dither5_1_\\@:;"
+ " nop;" // nop to keep timing in line
+ " .else;"
+ " adds \\bn, \\d;" // do the add"
+ " bcc dither5_2_\\@;"
+ " mvns \\bn, \\bn;" // set the low 24bits ot 1's
+ " dither5_2_\\@:;"
+ " bcc dither5_3_\\@;"
+ " lsls \\bn, \\bn, #24;" // move low 8 bits to the high bits
+ " dither5_3_\\@:;"
+ " .endif;"
+ " .syntax divided;"
+ " .endm;"
+
+ // Do our scaling
+ " .macro scale4 bn, base, scale, scratch;"
+ " ldrb \\scratch, [\\base, \\scale];"
+ " lsr \\bn, \\bn, #24;" // bring bn back down to its low 8 bits
+ " mul \\bn, \\scratch;" // do the multiply
+ " .endm;"
+
+ // swap bn into b
+ " .macro swapbbn1 b,bn;"
+ " lsl \\b, \\bn, #16;" // put the 8 bits we want for output high
+ " .endm;"
+
+ // adjust the dithering value for the next time around (load e from memory
+ // to do the math)
+ " .macro adjdither7 base,d,rled,eoffset,scratch;"
+ " ldrb \\d, [\\base, \\rled];"
+ " ldrb \\scratch,[\\base,\\eoffset];" // load e
+ " .syntax unified;"
+ " subs \\d, \\scratch, \\d;" // d=e-d
+ " .syntax divided;"
+ " strb \\d, [\\base, \\rled];" // save d
+ " .endm;"
+
+ // increment the led pointer (base+9 has what we're incrementing by)
+ " .macro incleds3 leds, base, scratch;"
+ " ldrb \\scratch, [\\base, #9];" // load incremen
+ " add \\leds, \\leds, \\scratch;" // update leds pointer
+ " .endm;"
+
+ // compare and loop
+ " .macro cmploop5 counter,label;"
+ " .syntax unified;"
+ " subs \\counter, #1;"
+ " .syntax divided;"
+ " beq done_\\@;"
+ " m0pad;"
+ " b \\label;"
+ " done_\\@:;"
+ " .endm;"
+
+ " .endif;"
+ );
+
+#define M0_ASM_ARGS : \
+ [leds] "+h" (leds), \
+ [counter] "+l" (counter), \
+ [scratch] "+l" (scratch), \
+ [d] "+l" (d), \
+ [bn] "+l" (bn), \
+ [b] "+l" (b) \
+ : \
+ [port] "l" (port), \
+ [base] "l" (base), \
+ [bitmask] "l" (bitmask), \
+ [hi_off] "I" (HI_OFFSET), \
+ [lo_off] "I" (LO_OFFSET), \
+ [led0] "I" (RO(0)), \
+ [led1] "I" (RO(1)), \
+ [led2] "I" (RO(2)), \
+ [scale0] "I" (3+RO(0)), \
+ [scale1] "I" (3+RO(1)), \
+ [scale2] "I" (3+RO(2)), \
+ [e0] "I" (6+RO(0)), \
+ [e1] "I" (6+RO(1)), \
+ [e2] "I" (6+RO(2)), \
+ [T1] "I" (T1), \
+ [T2] "I" (T2), \
+ [T3] "I" (T3) \
+ :
+
+ /////////////////////////////////////////////////////////////////////////
+ // now for some convinience macros to make building our lines a bit cleaner
+#define LOOP " loop_%=:"
+#define HI2 " qset2 %[bitmask], %[port], %[hi_off];"
+#define D1 " mod_delay %c[T1],2,0,%[scratch];"
+#define QLO4 " qlo4 %[b],%[bitmask],%[port], %[lo_off];"
+#define LOADLEDS3(X) " loadleds3 %[leds], %[bn], %[led" #X "] ,%[scratch];"
+#define D2(ADJ) " mod_delay %c[T2],4," #ADJ ",%[scratch];"
+#define LO2 " qset2 %[bitmask], %[port], %[lo_off];"
+#define D3(ADJ) " mod_delay %c[T3],2," #ADJ ",%[scratch];"
+#define LOADDITHER7(X) " loaddither7 %[bn], %[d], %[base], %[led" #X "];"
+#define DITHER5 " dither5 %[bn], %[d];"
+#define SCALE4(X) " scale4 %[bn], %[base], %[scale" #X "], %[scratch];"
+#define SWAPBBN1 " swapbbn1 %[b], %[bn];"
+#define ADJDITHER7(X) " adjdither7 %[base],%[d],%[led" #X "],%[e" #X "],%[scratch];"
+#define INCLEDS3 " incleds3 %[leds],%[base],%[scratch];"
+#define CMPLOOP5 " cmploop5 %[counter], loop_%=;"
+#define NOTHING ""
+
+#if !(defined(SEI_CHK) && (FASTLED_ALLOW_INTERRUPTS == 1))
+ // We're not allowing interrupts - run the entire loop in asm to keep things
+ // as tight as possible. In an ideal world, we should be pushing out ws281x
+ // leds (or other 3-wire leds) with zero gaps between pixels.
+ asm __volatile__ (
+ // pre-load byte 0
+ LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1
+
+ // loop over writing out the data
+ LOOP
+ // Write out byte 0, prepping byte 1
+ HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0)
+ HI2 D1 QLO4 LOADLEDS3(1) D2(3) LO2 D3(0)
+ HI2 D1 QLO4 LOADDITHER7(1) D2(7) LO2 D3(0)
+ HI2 D1 QLO4 DITHER5 D2(5) LO2 D3(0)
+ HI2 D1 QLO4 SCALE4(1) D2(4) LO2 D3(0)
+ HI2 D1 QLO4 ADJDITHER7(1) D2(7) LO2 D3(0)
+ HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0)
+ HI2 D1 QLO4 SWAPBBN1 D2(1) LO2 D3(0)
+
+ // Write out byte 1, prepping byte 2
+ HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0)
+ HI2 D1 QLO4 LOADLEDS3(2) D2(3) LO2 D3(0)
+ HI2 D1 QLO4 LOADDITHER7(2) D2(7) LO2 D3(0)
+ HI2 D1 QLO4 DITHER5 D2(5) LO2 D3(0)
+ HI2 D1 QLO4 SCALE4(2) D2(4) LO2 D3(0)
+ HI2 D1 QLO4 ADJDITHER7(2) D2(7) LO2 D3(0)
+ HI2 D1 QLO4 INCLEDS3 D2(3) LO2 D3(0)
+ HI2 D1 QLO4 SWAPBBN1 D2(1) LO2 D3(0)
+
+ // Write out byte 2, prepping byte 0
+ HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0)
+ HI2 D1 QLO4 LOADLEDS3(0) D2(3) LO2 D3(0)
+ HI2 D1 QLO4 LOADDITHER7(0) D2(7) LO2 D3(0)
+ HI2 D1 QLO4 DITHER5 D2(5) LO2 D3(0)
+ HI2 D1 QLO4 SCALE4(0) D2(4) LO2 D3(0)
+ HI2 D1 QLO4 ADJDITHER7(0) D2(7) LO2 D3(0)
+ HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0)
+ HI2 D1 QLO4 SWAPBBN1 D2(1) LO2 D3(5) CMPLOOP5
+
+ M0_ASM_ARGS
+ );
+#else
+ // We're allowing interrupts - track the loop outside the asm code, to allow
+ // inserting the interrupt overrun checks.
+ asm __volatile__ (
+ // pre-load byte 0
+ LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1
+ M0_ASM_ARGS);
+
+ do {
+ asm __volatile__ (
+ // Write out byte 0, prepping byte 1
+ HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0)
+ HI2 D1 QLO4 LOADLEDS3(1) D2(3) LO2 D3(0)
+ HI2 D1 QLO4 LOADDITHER7(1) D2(7) LO2 D3(0)
+ HI2 D1 QLO4 DITHER5 D2(5) LO2 D3(0)
+ HI2 D1 QLO4 SCALE4(1) D2(4) LO2 D3(0)
+ HI2 D1 QLO4 ADJDITHER7(1) D2(7) LO2 D3(0)
+ HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0)
+ HI2 D1 QLO4 SWAPBBN1 D2(1) LO2 D3(0)
+
+ // Write out byte 1, prepping byte 2
+ HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0)
+ HI2 D1 QLO4 LOADLEDS3(2) D2(3) LO2 D3(0)
+ HI2 D1 QLO4 LOADDITHER7(2) D2(7) LO2 D3(0)
+ HI2 D1 QLO4 DITHER5 D2(5) LO2 D3(0)
+ HI2 D1 QLO4 SCALE4(2) D2(4) LO2 D3(0)
+ HI2 D1 QLO4 ADJDITHER7(2) D2(7) LO2 D3(0)
+ HI2 D1 QLO4 INCLEDS3 D2(3) LO2 D3(0)
+ HI2 D1 QLO4 SWAPBBN1 D2(1) LO2 D3(0)
+
+ // Write out byte 2, prepping byte 0
+ HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0)
+ HI2 D1 QLO4 LOADLEDS3(0) D2(3) LO2 D3(0)
+ HI2 D1 QLO4 LOADDITHER7(0) D2(7) LO2 D3(0)
+ HI2 D1 QLO4 DITHER5 D2(5) LO2 D3(0)
+ HI2 D1 QLO4 SCALE4(0) D2(4) LO2 D3(0)
+ HI2 D1 QLO4 ADJDITHER7(0) D2(7) LO2 D3(0)
+ HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0)
+ HI2 D1 QLO4 SWAPBBN1 D2(1) LO2 D3(5)
+
+ M0_ASM_ARGS
+ );
+ SEI_CHK; INNER_SEI; --counter; CLI_CHK;
+ } while(counter);
+#endif
+ return num_leds;
+}
+
+#endif
diff --git a/platforms/arm/d21/clockless_arm_d21.h b/platforms/arm/d21/clockless_arm_d21.h
new file mode 100644
index 00000000..c6ca1792
--- /dev/null
+++ b/platforms/arm/d21/clockless_arm_d21.h
@@ -0,0 +1,89 @@
+#ifndef __INC_CLOCKLESS_ARM_D21
+#define __INC_CLOCKLESS_ARM_D21
+
+#include "platforms/arm/common/m0clockless.h"
+FASTLED_NAMESPACE_BEGIN
+#define FASTLED_HAS_CLOCKLESS 1
+
+template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
+class ClocklessController : public CLEDController {
+ typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
+ typedef typename FastPinBB<DATA_PIN>::port_t data_t;
+
+ data_t mPinMask;
+ data_ptr_t mPort;
+ CMinWait<WAIT_TIME> mWait;
+public:
+ virtual void init() {
+ FastPinBB<DATA_PIN>::setOutput();
+ mPinMask = FastPinBB<DATA_PIN>::mask();
+ mPort = FastPinBB<DATA_PIN>::port();
+ }
+
+ virtual uint16_t getMaxRefreshRate() const { return 400; }
+
+ virtual void clearLeds(int nLeds) {
+ showColor(CRGB(0, 0, 0), nLeds, 0);
+ }
+
+ // set all the leds on the controller to a given color
+ virtual void showColor(const struct CRGB & rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+ mWait.wait();
+ cli();
+
+ showRGBInternal(pixels);
+
+ sei();
+ mWait.mark();
+ }
+
+ virtual void show(const struct CRGB *rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+ mWait.wait();
+ cli();
+
+ showRGBInternal(pixels);
+
+ sei();
+ mWait.mark();
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void show(const struct CARGB *rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+ mWait.wait();
+ cli();
+ showRGBInternal(pixels);
+ sei();
+ mWait.mark();
+ }
+#endif
+
+ // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+ // gcc will use register Y for the this pointer.
+ static uint32_t showRGBInternal(PixelController<RGB_ORDER> & pixels) {
+ struct M0ClocklessData data;
+ data.d[0] = pixels.d[0];
+ data.d[1] = pixels.d[1];
+ data.d[2] = pixels.d[2];
+ data.s[0] = pixels.mScale[0];
+ data.s[1] = pixels.mScale[1];
+ data.s[2] = pixels.mScale[2];
+ data.e[0] = pixels.e[0];
+ data.e[1] = pixels.e[1];
+ data.e[2] = pixels.e[2];
+ data.adj = pixels.mAdvance;
+
+ typename FastPin<DATA_PIN>::port_ptr_t portBase = FastPin<DATA_PIN>::port();
+ showLedData<8,4,T1,T2,T3,RGB_ORDER, WAIT_TIME>(portBase, FastPin<DATA_PIN>::mask(), pixels.mData, pixels.mLen, &data);
+ return 0; // 0x00FFFFFF - _VAL;
+ }
+
+
+};
+
+FASTLED_NAMESPACE_END
+
+
+#endif // __INC_CLOCKLESS_ARM_D21
diff --git a/platforms/arm/d21/fastled_arm_d21.h b/platforms/arm/d21/fastled_arm_d21.h
new file mode 100644
index 00000000..3a62efc5
--- /dev/null
+++ b/platforms/arm/d21/fastled_arm_d21.h
@@ -0,0 +1,8 @@
+#ifndef __INC_FASTLED_ARM_D21_H
+#define __INC_FASTLED_ARM_D21_H
+
+#include "fastled_delay.h"
+#include "fastpin_arm_d21.h"
+#include "clockless_arm_d21.h"
+
+#endif
diff --git a/platforms/arm/d21/fastpin_arm_d21.h b/platforms/arm/d21/fastpin_arm_d21.h
new file mode 100644
index 00000000..9e9fe191
--- /dev/null
+++ b/platforms/arm/d21/fastpin_arm_d21.h
@@ -0,0 +1,95 @@
+#ifndef __INC_FASTPIN_ARM_SAM_H
+#define __INC_FASTPIN_ARM_SAM_H
+
+FASTLED_NAMESPACE_BEGIN
+
+#if defined(FASTLED_FORCE_SOFTWARE_PINS)
+#warning "Software pin support forced, pin access will be sloightly slower."
+#define NO_HARDWARE_PIN_SUPPORT
+#undef HAS_HARDWARE_PIN_SUPPORT
+
+#else
+
+/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this
+/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
+/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
+/// The registers are data output, set output, clear output, toggle output, input, and direction
+
+template<uint8_t PIN, uint8_t _BIT, uint32_t _MASK, int _GRP> class _ARMPIN {
+public:
+ typedef volatile uint32_t * port_ptr_t;
+ typedef uint32_t port_t;
+
+ #if 0
+ inline static void setOutput() {
+ if(_BIT<8) {
+ _CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4));
+ } else {
+ _CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4));
+ }
+ }
+ inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
+ #endif
+
+ inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
+ inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
+
+ inline static void hi() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTSET.reg = _MASK; }
+ inline static void lo() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTCLR.reg = _MASK; }
+ // inline static void lo() __attribute__ ((always_inline)) { PORT->Group[_GRP].BSRR = (_MASK<<16); }
+ inline static void set(register port_t val) __attribute__ ((always_inline)) { PORT->Group[_GRP].OUT.reg = val; }
+
+ inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
+
+ inline static void toggle() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTTGL.reg = _MASK; }
+
+ inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
+ inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
+ inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
+
+ inline static port_t hival() __attribute__ ((always_inline)) { return PORT->Group[_GRP].OUT.reg | _MASK; }
+ inline static port_t loval() __attribute__ ((always_inline)) { return PORT->Group[_GRP].OUT.reg & ~_MASK; }
+ inline static port_ptr_t port() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUT.reg; }
+ inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUTSET.reg; }
+ inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUTCLR.reg; }
+ inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
+};
+
+#define _R(T) struct __gen_struct_ ## T
+#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline volatile PortGroup * r() { return T; } };
+
+#define _IO32(L) _RD32(GPIO ## L)
+
+#define _DEFPIN_ARM(PIN, L, BIT) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1 << BIT, L> {};
+
+// Actual pin definitions
+#if defined(ARDUINO_SAMD_ZERO)
+
+#define MAX_PIN 42
+_DEFPIN_ARM( 0,0,10); _DEFPIN_ARM( 1,0,11); _DEFPIN_ARM( 2,0, 8); _DEFPIN_ARM( 3,0, 9);
+_DEFPIN_ARM( 4,0,14); _DEFPIN_ARM( 5,0,15); _DEFPIN_ARM( 6,0,20); _DEFPIN_ARM( 7,0,21);
+_DEFPIN_ARM( 8,0, 6); _DEFPIN_ARM( 9,0, 7); _DEFPIN_ARM(10,0,18); _DEFPIN_ARM(11,0,16);
+_DEFPIN_ARM(12,0,19); _DEFPIN_ARM(13,0,17); _DEFPIN_ARM(14,0, 2); _DEFPIN_ARM(15,1, 8);
+_DEFPIN_ARM(16,1, 9); _DEFPIN_ARM(17,0, 4); _DEFPIN_ARM(18,0, 5); _DEFPIN_ARM(19,1, 2);
+_DEFPIN_ARM(20,0,22); _DEFPIN_ARM(21,0,23); _DEFPIN_ARM(22,0,12); _DEFPIN_ARM(23,1,11);
+_DEFPIN_ARM(24,1,10); _DEFPIN_ARM(25,1, 3); _DEFPIN_ARM(26,0,27); _DEFPIN_ARM(27,0,28);
+_DEFPIN_ARM(28,0,24); _DEFPIN_ARM(29,0,25); _DEFPIN_ARM(30,1,22); _DEFPIN_ARM(31,1,23);
+_DEFPIN_ARM(32,0,22); _DEFPIN_ARM(33,0,23); _DEFPIN_ARM(34,0,19); _DEFPIN_ARM(35,0,16);
+_DEFPIN_ARM(36,0,18); _DEFPIN_ARM(37,0,17); _DEFPIN_ARM(38,0,13); _DEFPIN_ARM(39,0,21);
+_DEFPIN_ARM(40,0, 6); _DEFPIN_ARM(41,0, 7); _DEFPIN_ARM(42,0, 3);
+
+#define SPI_DATA 24
+#define SPI_CLOCK 23
+
+#define HAS_HARDWARE_PIN_SUPPORT
+
+#endif
+
+
+
+#endif // FASTLED_FORCE_SOFTWARE_PINS
+
+FASTLED_NAMESPACE_END
+
+
+#endif // __INC_FASTPIN_ARM_SAM_H
diff --git a/platforms/arm/d21/led_sysdefs_arm_d21.h b/platforms/arm/d21/led_sysdefs_arm_d21.h
new file mode 100644
index 00000000..43c406e4
--- /dev/null
+++ b/platforms/arm/d21/led_sysdefs_arm_d21.h
@@ -0,0 +1,26 @@
+#ifndef __INC_LED_SYSDEFS_ARM_D21_H
+#define __INC_LED_SYSDEFS_ARM_D21_H
+
+
+#define FASTLED_ARM
+#define FASTLED_ARM_M0_PLUS
+
+#ifndef INTERRUPT_THRESHOLD
+#define INTERRUPT_THRESHOLD 1
+#endif
+
+// Default to allowing interrupts
+#ifndef FASTLED_ALLOW_INTERRUPTS
+#define FASTLED_ALLOW_INTERRUPTS 0
+#endif
+
+#if FASTLED_ALLOW_INTERRUPTS == 1
+#define FASTLED_ACCURATE_CLOCK
+#endif
+
+// reuseing/abusing cli/sei defs for due
+#define cli() __disable_irq();
+#define sei() __enable_irq();
+
+
+#endif
diff --git a/clockless_arm_k20.h b/platforms/arm/k20/clockless_arm_k20.h
index 126955b1..f17ba72d 100644
--- a/clockless_arm_k20.h
+++ b/platforms/arm/k20/clockless_arm_k20.h
@@ -1,10 +1,15 @@
#ifndef __INC_CLOCKLESS_ARM_K20_H
#define __INC_CLOCKLESS_ARM_K20_H
+FASTLED_NAMESPACE_BEGIN
+
// Definition for a single channel clockless controller for the k20 family of chips, like that used in the teensy 3.0/3.1
// See clockless.h for detailed info on how the template parameters are used.
#if defined(FASTLED_TEENSY3)
-template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 500>
+
+#define FASTLED_HAS_CLOCKLESS 1
+
+template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class ClocklessController : public CLEDController {
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPin<DATA_PIN>::port_t data_t;
@@ -19,6 +24,8 @@ public:
mPort = FastPin<DATA_PIN>::port();
}
+ virtual uint16_t getMaxRefreshRate() const { return 400; }
+
virtual void clearLeds(int nLeds) {
showColor(CRGB(0, 0, 0), nLeds, 0);
}
@@ -30,14 +37,7 @@ protected:
PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
mWait.wait();
- cli();
-
- uint32_t clocks = showRGBInternal(pixels);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS(clocks);
- MS_COUNTER += (1 + (microsTaken / 1000));
- sei();
+ showRGBInternal(pixels);
mWait.mark();
}
@@ -45,14 +45,7 @@ protected:
PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
mWait.wait();
- cli();
-
- uint32_t clocks = showRGBInternal(pixels);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS(clocks);
- MS_COUNTER += (1 + (microsTaken / 1000));
- sei();
+ showRGBInternal(pixels);
mWait.mark();
}
@@ -60,15 +53,7 @@ protected:
virtual void show(const struct CARGB *rgbdata, int nLeds, CRGB scale) {
PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
mWait.wait();
- cli();
-
- uint32_t clocks = showRGBInternal(pixels);
-
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS(clocks);
- MS_COUNTER += (1 + (microsTaken / 1000));
- sei();
+ showRGBInternal(pixels);
mWait.mark();
}
#endif
@@ -118,11 +103,21 @@ protected:
pixels.preStepFirstByteDithering();
register uint8_t b = pixels.loadAndScale0();
+ cli();
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
while(pixels.has(1)) {
pixels.stepDithering();
+ #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ cli();
+ // if interrupts took longer than 45µs, punt on the current frame
+ if(ARM_DWT_CYCCNT > next_mark) {
+ if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; }
+ }
+ hi = *port | FastPin<DATA_PIN>::mask();
+ lo = *port & ~FastPin<DATA_PIN>::mask();
+ #endif
// Write first byte, read next byte
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.loadAndScale1();
@@ -134,11 +129,17 @@ protected:
// Write third byte, read 1st byte of next pixel
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.advanceAndLoadAndScale0();
+ #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ sei();
+ #endif
};
+ sei();
return ARM_DWT_CYCCNT;
}
};
#endif
+FASTLED_NAMESPACE_END
+
#endif
diff --git a/platforms/arm/k20/clockless_block_arm_k20.h b/platforms/arm/k20/clockless_block_arm_k20.h
new file mode 100644
index 00000000..385ee57f
--- /dev/null
+++ b/platforms/arm/k20/clockless_block_arm_k20.h
@@ -0,0 +1,396 @@
+#ifndef __INC_BLOCK_CLOCKLESS_ARM_K20_H
+#define __INC_BLOCK_CLOCKLESS_ARM_K20_H
+
+// Definition for a single channel clockless controller for the k20 family of chips, like that used in the teensy 3.0/3.1
+// See clockless.h for detailed info on how the template parameters are used.
+#if defined(FASTLED_TEENSY3)
+#define FASTLED_HAS_BLOCKLESS 1
+
+#define PORTC_FIRST_PIN 15
+#define PORTD_FIRST_PIN 2
+#define HAS_PORTDC 1
+
+#define PORT_MASK (((1<<LANES)-1) & ((FIRST_PIN==2) ? 0xFF : 0xFFF))
+
+#define MIN(X,Y) (((X)<(Y)) ? (X):(Y))
+#define LANES ((FIRST_PIN==2) ? MIN(__LANES,8) : MIN(__LANES,12))
+
+#include "kinetis.h"
+
+FASTLED_NAMESPACE_BEGIN
+
+template <uint8_t __LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 40>
+class InlineBlockClocklessController : public CLEDController {
+ typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t;
+ typedef typename FastPin<FIRST_PIN>::port_t data_t;
+
+ data_t mPinMask;
+ data_ptr_t mPort;
+ CMinWait<WAIT_TIME> mWait;
+public:
+ virtual void init() {
+ if(FIRST_PIN == PORTC_FIRST_PIN) { // PORTC
+ switch(LANES) {
+ case 12: FastPin<30>::setOutput();
+ case 11: FastPin<29>::setOutput();
+ case 10: FastPin<27>::setOutput();
+ case 9: FastPin<28>::setOutput();
+ case 8: FastPin<12>::setOutput();
+ case 7: FastPin<11>::setOutput();
+ case 6: FastPin<13>::setOutput();
+ case 5: FastPin<10>::setOutput();
+ case 4: FastPin<9>::setOutput();
+ case 3: FastPin<23>::setOutput();
+ case 2: FastPin<22>::setOutput();
+ case 1: FastPin<15>::setOutput();
+ }
+ } else if(FIRST_PIN == PORTD_FIRST_PIN) { // PORTD
+ switch(LANES) {
+ case 8: FastPin<5>::setOutput();
+ case 7: FastPin<21>::setOutput();
+ case 6: FastPin<20>::setOutput();
+ case 5: FastPin<6>::setOutput();
+ case 4: FastPin<8>::setOutput();
+ case 3: FastPin<7>::setOutput();
+ case 2: FastPin<14>::setOutput();
+ case 1: FastPin<2>::setOutput();
+ }
+ }
+ mPinMask = FastPin<FIRST_PIN>::mask();
+ mPort = FastPin<FIRST_PIN>::port();
+ }
+
+ virtual uint16_t getMaxRefreshRate() const { return 400; }
+
+ virtual void clearLeds(int nLeds) {
+ showColor(CRGB(0, 0, 0), nLeds, 0);
+ }
+
+ // set all the leds on the controller to a given color
+ virtual void showColor(const struct CRGB & rgbdata, int nLeds, CRGB scale) {
+ MultiPixelController<LANES,PORT_MASK,RGB_ORDER> pixels(rgbdata,nLeds, scale, getDither() );
+ mWait.wait();
+ uint32_t clocks = showRGBInternal(pixels,nLeds);
+ #if FASTLED_ALLOW_INTTERUPTS == 0
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS(clocks);
+ MS_COUNTER += (1 + (microsTaken / 1000));
+ #endif
+
+ mWait.mark();
+ }
+
+ virtual void show(const struct CRGB *rgbdata, int nLeds, CRGB scale) {
+ MultiPixelController<LANES,PORT_MASK,RGB_ORDER> pixels(rgbdata,nLeds, scale, getDither() );
+ mWait.wait();
+ uint32_t clocks = showRGBInternal(pixels,nLeds);
+ #if FASTLED_ALLOW_INTTERUPTS == 0
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS(clocks);
+ MS_COUNTER += (1 + (microsTaken / 1000));
+ #endif
+
+ mWait.mark();
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void show(const struct CARGB *rgbdata, int nLeds, CRGB scale) {
+ MultiPixelController<LANES,PORT_MASK,RGB_ORDER> pixels(rgbdata,nLeds, scale, getDither() );
+ mWait.wait();
+ uint32_t clocks = showRGBInternal(pixels,nLeds);
+
+ #if FASTLED_ALLOW_INTTERUPTS == 0
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS(clocks);
+ MS_COUNTER += (1 + (microsTaken / 1000));
+ #endif
+
+ mWait.mark();
+ }
+#endif
+
+
+ typedef union {
+ uint8_t bytes[12];
+ uint16_t shorts[6];
+ uint32_t raw[3];
+ } Lines;
+
+ template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, MultiPixelController<LANES, PORT_MASK, RGB_ORDER> &pixels) { // , register uint32_t & b2) {
+ register Lines b2;
+ if(LANES>8) {
+ transpose8<1,2>(b.bytes,b2.bytes);
+ transpose8<1,2>(b.bytes+8,b2.bytes+1);
+ } else {
+ transpose8x1(b.bytes,b2.bytes);
+ }
+ register uint8_t d = pixels.template getd<PX>(pixels);
+ register uint8_t scale = pixels.template getscale<PX>(pixels);
+
+ for(register uint32_t i = 0; i < (LANES/2); i++) {
+ while(ARM_DWT_CYCCNT < next_mark);
+ next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
+ *FastPin<FIRST_PIN>::sport() = PORT_MASK;
+
+ while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
+ if(LANES>8) {
+ *FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
+ } else {
+ *FastPin<FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PORT_MASK);
+ }
+
+ while((next_mark - ARM_DWT_CYCCNT) > (T3));
+ *FastPin<FIRST_PIN>::cport() = PORT_MASK;
+
+ b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
+ b.bytes[i+(LANES/2)] = pixels.template loadAndScale<PX>(pixels,i+(LANES/2),d,scale);
+ }
+
+ // if folks use an odd numnber of lanes, get the last byte's value here
+ if(LANES & 0x01) {
+ b.bytes[LANES-1] = pixels.template loadAndScale<PX>(pixels,LANES-1,d,scale);
+ }
+
+ for(register uint32_t i = LANES/2; i < 8; i++) {
+ while(ARM_DWT_CYCCNT < next_mark);
+ next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
+ *FastPin<FIRST_PIN>::sport() = PORT_MASK;
+ while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
+ if(LANES>8) {
+ *FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
+ } else {
+ // b2.bytes[0] = 0;
+ *FastPin<FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PORT_MASK);
+ }
+
+ while((next_mark - ARM_DWT_CYCCNT) > (T3));
+ *FastPin<FIRST_PIN>::cport() = PORT_MASK;
+
+ }
+
+
+ // while(ARM_DWT_CYCCNT < next_mark);
+ // next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
+ // *FastPin<FIRST_PIN>::sport() = PORT_MASK;
+ //
+ // while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+6));
+ // if(LANES>8) {
+ // *FastPin<FIRST_PIN>::cport() = ((~b2.shorts[7]) & PORT_MASK);
+ // } else {
+ // *FastPin<FIRST_PIN>::cport() = PORT_MASK; // ((~b2.bytes[7-i]) & PORT_MASK);
+ // }
+ //
+ // while((next_mark - ARM_DWT_CYCCNT) > (T3));
+ // *FastPin<FIRST_PIN>::cport() = PORT_MASK;
+ }
+
+
+
+ // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+ // gcc will use register Y for the this pointer.
+ static uint32_t showRGBInternal(MultiPixelController<LANES, PORT_MASK, RGB_ORDER> &allpixels, int nLeds) {
+ // Get access to the clock
+ ARM_DEMCR |= ARM_DEMCR_TRCENA;
+ ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
+ ARM_DWT_CYCCNT = 0;
+
+ // Setup the pixel controller and load/scale the first byte
+ allpixels.preStepFirstByteDithering();
+ register Lines b0;
+
+ allpixels.preStepFirstByteDithering();
+ for(int i = 0; i < LANES; i++) {
+ b0.bytes[i] = allpixels.loadAndScale0(i);
+ }
+
+ cli();
+ uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
+
+ while(nLeds--) {
+ #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ cli();
+ // if interrupts took longer than 45µs, punt on the current frame
+ if(ARM_DWT_CYCCNT > next_mark) {
+ if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-5)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; }
+ }
+ #endif
+ allpixels.stepDithering();
+
+ // Write first byte, read next byte
+ writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
+
+ // Write second byte, read 3rd byte
+ writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
+ allpixels.advanceData();
+
+ // Write third byte
+ writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
+ #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ sei();
+ #endif
+ };
+
+ return ARM_DWT_CYCCNT;
+ }
+};
+
+#define DLANES (MIN(__LANES,16))
+#define PMASK ((1<<(DLANES))-1)
+#define PMASK_HI (PMASK>>8 & 0xFF)
+#define PMASK_LO (PMASK & 0xFF)
+
+template <uint8_t __LANES, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
+class SixteenWayInlineBlockClocklessController : public CLEDController {
+ typedef typename FastPin<PORTC_FIRST_PIN>::port_ptr_t data_ptr_t;
+ typedef typename FastPin<PORTC_FIRST_PIN>::port_t data_t;
+
+ data_t mPinMask;
+ data_ptr_t mPort;
+ CMinWait<WAIT_TIME> mWait;
+public:
+ virtual void init() {
+ // FastPin<30>::setOutput();
+ // FastPin<29>::setOutput();
+ // FastPin<27>::setOutput();
+ // FastPin<28>::setOutput();
+ switch(DLANES) {
+ case 16: FastPin<12>::setOutput();
+ case 15: FastPin<11>::setOutput();
+ case 14: FastPin<13>::setOutput();
+ case 13: FastPin<10>::setOutput();
+ case 12: FastPin<9>::setOutput();
+ case 11: FastPin<23>::setOutput();
+ case 10: FastPin<22>::setOutput();
+ case 9: FastPin<15>::setOutput();
+
+ case 8: FastPin<5>::setOutput();
+ case 7: FastPin<21>::setOutput();
+ case 6: FastPin<20>::setOutput();
+ case 5: FastPin<6>::setOutput();
+ case 4: FastPin<8>::setOutput();
+ case 3: FastPin<7>::setOutput();
+ case 2: FastPin<14>::setOutput();
+ case 1: FastPin<2>::setOutput();
+ }
+ }
+
+ virtual void clearLeds(int nLeds) {
+ showColor(CRGB(0, 0, 0), nLeds, 0);
+ }
+
+ // set all the leds on the controller to a given color
+ virtual void showColor(const struct CRGB & rgbdata, int nLeds, CRGB scale) {
+ MultiPixelController<DLANES,PMASK,RGB_ORDER> pixels(rgbdata,nLeds, scale, getDither() );
+ mWait.wait();
+ showRGBInternal(pixels,nLeds);
+ mWait.mark();
+ }
+
+ virtual void show(const struct CRGB *rgbdata, int nLeds, CRGB scale) {
+ MultiPixelController<DLANES,PMASK,RGB_ORDER> pixels(rgbdata,nLeds, scale, getDither() );
+ mWait.wait();
+ showRGBInternal(pixels,nLeds);
+ mWait.mark();
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void show(const struct CARGB *rgbdata, int nLeds, CRGB scale) {
+ MultiPixelController<DLANES,PMASK,RGB_ORDER> pixels(rgbdata,nLeds, scale, getDither() );
+ mWait.wait();
+ showRGBInternal(pixels,nLeds);
+ mWait.mark();
+ }
+#endif
+
+
+ typedef union {
+ uint8_t bytes[16];
+ uint16_t shorts[8];
+ uint32_t raw[4];
+ } Lines;
+
+ template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, MultiPixelController<DLANES, PMASK, RGB_ORDER> &pixels) { // , register uint32_t & b2) {
+ register Lines b2;
+ transpose8x1(b.bytes,b2.bytes);
+ transpose8x1(b.bytes+8,b2.bytes+8);
+ register uint8_t d = pixels.template getd<PX>(pixels);
+ register uint8_t scale = pixels.template getscale<PX>(pixels);
+
+ for(register uint32_t i = 0; (i < DLANES) && (i < 8); i++) {
+ while(ARM_DWT_CYCCNT < next_mark);
+ next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
+ *FastPin<PORTD_FIRST_PIN>::sport() = PMASK_LO;
+ *FastPin<PORTC_FIRST_PIN>::sport() = PMASK_HI;
+
+ while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+6));
+ *FastPin<PORTD_FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PMASK_LO);
+ *FastPin<PORTC_FIRST_PIN>::cport() = ((~b2.bytes[15-i]) & PMASK_HI);
+
+ while((next_mark - ARM_DWT_CYCCNT) > (T3));
+ *FastPin<PORTD_FIRST_PIN>::cport() = PMASK_LO;
+ *FastPin<PORTC_FIRST_PIN>::cport() = PMASK_HI;
+
+ b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
+ if(DLANES==16 || (DLANES>8 && ((i+8) < DLANES))) {
+ b.bytes[i+8] = pixels.template loadAndScale<PX>(pixels,i+8,d,scale);
+ }
+ }
+ }
+
+
+
+ // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+ // gcc will use register Y for the this pointer.
+ static uint32_t showRGBInternal(MultiPixelController<DLANES, PMASK, RGB_ORDER> &allpixels, int nLeds) {
+ // Get access to the clock
+ ARM_DEMCR |= ARM_DEMCR_TRCENA;
+ ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
+ ARM_DWT_CYCCNT = 0;
+
+ // Setup the pixel controller and load/scale the first byte
+ allpixels.preStepFirstByteDithering();
+ register Lines b0;
+
+ allpixels.preStepFirstByteDithering();
+ for(int i = 0; i < DLANES; i++) {
+ b0.bytes[i] = allpixels.loadAndScale0(i);
+ }
+
+ cli();
+ uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
+
+ while(nLeds--) {
+ allpixels.stepDithering();
+ #if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
+ cli();
+ // if interrupts took longer than 45µs, punt on the current frame
+ if(ARM_DWT_CYCCNT > next_mark) {
+ if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; }
+ }
+ #endif
+
+ // Write first byte, read next byte
+ writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
+
+ // Write second byte, read 3rd byte
+ writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
+ allpixels.advanceData();
+
+ // Write third byte
+ writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
+
+ #if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
+ sei();
+ #endif
+ };
+ sei();
+
+ return ARM_DWT_CYCCNT;
+ }
+};
+
+FASTLED_NAMESPACE_END
+
+#endif
+
+#endif
diff --git a/platforms/arm/k20/fastled_arm_k20.h b/platforms/arm/k20/fastled_arm_k20.h
new file mode 100644
index 00000000..7220aef2
--- /dev/null
+++ b/platforms/arm/k20/fastled_arm_k20.h
@@ -0,0 +1,14 @@
+#ifndef __INC_FASTLED_ARM_K20_H
+#define __INC_FASTLED_ARM_K20_H
+
+// Include the k20 headers
+#include "bitswap.h"
+#include "fastled_delay.h"
+#include "fastpin_arm_k20.h"
+#include "fastspi_arm_k20.h"
+#include "octows2811_controller.h"
+#include "smartmatrix_t3.h"
+#include "clockless_arm_k20.h"
+#include "clockless_block_arm_k20.h"
+
+#endif
diff --git a/fastpin_arm_k20.h b/platforms/arm/k20/fastpin_arm_k20.h
index 71bddde3..b26e5607 100644
--- a/fastpin_arm_k20.h
+++ b/platforms/arm/k20/fastpin_arm_k20.h
@@ -1,11 +1,21 @@
#ifndef __FASTPIN_ARM_K20_H
#define __FASTPIN_ARM_K20_H
+FASTLED_NAMESPACE_BEGIN
+
+#if defined(FASTLED_FORCE_SOFTWARE_PINS)
+#warning "Software pin support forced, pin access will be sloightly slower."
+#define NO_HARDWARE_PIN_SUPPORT
+#undef HAS_HARDWARE_PIN_SUPPORT
+
+#else
+
+
/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
/// The registers are data output, set output, clear output, toggle output, input, and direction
-template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN {
+template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN {
public:
typedef volatile uint32_t * port_ptr_t;
typedef uint32_t port_t;
@@ -18,7 +28,7 @@ public:
inline static void set(register port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
-
+
inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
@@ -28,12 +38,14 @@ public:
inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
+ inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
+ inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
};
-/// Template definition for teensy 3.0 style ARM pins using bit banding, providing direct access to the various GPIO registers. GCC
+/// Template definition for teensy 3.0 style ARM pins using bit banding, providing direct access to the various GPIO registers. GCC
/// does a poor job of optimizing around these accesses so they are not being used just yet.
-template<uint8_t PIN, int _BIT, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN_BITBAND {
+template<uint8_t PIN, int _BIT, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN_BITBAND {
public:
typedef volatile uint32_t * port_ptr_t;
typedef uint32_t port_t;
@@ -46,7 +58,7 @@ public:
inline static void set(register port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
-
+
inline static void toggle() __attribute__ ((always_inline)) { *_PTOR::template rx<_BIT>() = 1; }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
@@ -63,6 +75,7 @@ public:
#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))
+#define _R(T) struct __gen_struct_ ## T
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } \
template<int BIT> static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
#define _IO32(L) _RD32(GPIO ## L ## _PDOR); _RD32(GPIO ## L ## _PSOR); _RD32(GPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(GPIO ## L ## _PDIR); _RD32(GPIO ## L ## _PDDR);
@@ -70,9 +83,9 @@ public:
#define _DEFPIN_ARM(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {}; \
template<> class FastPinBB<PIN> : public _ARMPIN_BITBAND<PIN, BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
- _R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {};
+ _R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {};
-// Actual pin definitions
+// Actual pin definitions
#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
_IO32(A); _IO32(B); _IO32(C); _IO32(D); _IO32(E);
@@ -92,14 +105,16 @@ _DEFPIN_ARM(32, 18, B); _DEFPIN_ARM(33, 4, A);
#define SPI_CLOCK 13
#define SPI1 (*(SPI_t *)0x4002D000)
-#if defined(__MK20DX256__)
#define SPI2_DATA 7
#define SPI2_CLOCK 14
-#endif
#define FASTLED_TEENSY3
#define ARM_HARDWARE_SPI
#define HAS_HARDWARE_PIN_SUPPORT
#endif
-#endif
+#endif // FASTLED_FORCE_SOFTWARE_PINS
+
+FASTLED_NAMESPACE_END
+
+#endif // __INC_FASTPIN_ARM_K20
diff --git a/fastspi_arm_k20.h b/platforms/arm/k20/fastspi_arm_k20.h
index ad9598ae..f342caf8 100644
--- a/fastspi_arm_k20.h
+++ b/platforms/arm/k20/fastspi_arm_k20.h
@@ -1,6 +1,7 @@
#ifndef __INC_FASTSPI_ARM_H
#define __INC_FASTSPI_ARM_H
+FASTLED_NAMESPACE_BEGIN
#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
@@ -9,6 +10,10 @@
#define SPI_t KINETISK_SPI_t
#endif
+#ifndef KINETISK_SPI0
+#define KINETISK_SPI0 SPI0
+#endif
+
#ifndef SPI_PUSHR_CONT
#define SPI_PUSHR_CONT SPIX.PUSHR_CONT
#define SPI_PUSHR_CTAS(X) SPIX.PUSHR_CTAS(X)
@@ -21,61 +26,69 @@
// if no bits are set (value is 0), it returns 0, which is also the value returned if the lowest bit is the only bit
// set (the zero-th bit). Unclear if I will want this to change at some point.
template<int VAL, int BIT> class BitWork {
- public:
- static int highestBit() __attribute__((always_inline)) { return (VAL & 1 << BIT) ? BIT : BitWork<VAL, BIT-1>::highestBit(); }
+public:
+ static int highestBit() __attribute__((always_inline)) { return (VAL & 1 << BIT) ? BIT : BitWork<VAL, BIT-1>::highestBit(); }
};
template<int VAL> class BitWork<VAL, 0> {
- public:
- static int highestBit() __attribute__((always_inline)) { return 0; }
+public:
+ static int highestBit() __attribute__((always_inline)) { return 0; }
};
#define MAX(A, B) (( (A) > (B) ) ? (A) : (B))
#define USE_CONT 0
+// intra-frame backup data
+struct SPIState {
+ uint32_t _ctar0,_ctar1;
+ uint32_t pins[4];
+};
+
+// extern SPIState gState;
+
// Templated function to translate a clock divider value into the prescalar, scalar, and clock doubling setting for the world.
template <int VAL> void getScalars(uint32_t & preScalar, uint32_t & scalar, uint32_t & dbl) {
- switch(VAL) {
- // Handle the dbl clock cases
- case 0: case 1:
- case 2: preScalar = 0; scalar = 0; dbl = 1; break;
- case 3: preScalar = 1; scalar = 0; dbl = 1; break;
- case 5: preScalar = 2; scalar = 0; dbl = 1; break;
- case 7: preScalar = 3; scalar = 0; dbl = 1; break;
-
- // Handle the scalar value 6 cases (since it's not a power of two, it won't get caught
- // below)
- case 9: preScalar = 1; scalar = 2; dbl = 1; break;
- case 18: case 19: preScalar = 1; scalar = 2; dbl = 0; break;
-
- case 15: preScalar = 2; scalar = 2; dbl = 1; break;
- case 30: case 31: preScalar = 2; scalar = 2; dbl = 0; break;
-
- case 21: case 22: case 23: preScalar = 3; scalar = 2; dbl = 1; break;
- case 42: case 43: case 44: case 45: case 46: case 47: preScalar = 3; scalar = 2; dbl = 0; break;
- default: {
- int p2 = BitWork<VAL/2, 15>::highestBit();
- int p3 = BitWork<VAL/3, 15>::highestBit();
- int p5 = BitWork<VAL/5, 15>::highestBit();
- int p7 = BitWork<VAL/7, 15>::highestBit();
-
- int w2 = 2 * (1 << p2);
- int w3 = (VAL/3) > 0 ? 3 * (1 << p3) : 0;
- int w5 = (VAL/5) > 0 ? 5 * (1 << p5) : 0;
- int w7 = (VAL/7) > 0 ? 7 * (1 << p7) : 0;
-
- int maxval = MAX(MAX(w2, w3), MAX(w5, w7));
-
- if(w2 == maxval) { preScalar = 0; scalar = p2; }
- else if(w3 == maxval) { preScalar = 1; scalar = p3; }
- else if(w5 == maxval) { preScalar = 2; scalar = p5; }
- else if(w7 == maxval) { preScalar = 3; scalar = p7; }
-
- dbl = 0;
- if(scalar == 0) { dbl = 1; }
- else if(scalar < 3) { scalar--; }
- }
- }
+ switch(VAL) {
+ // Handle the dbl clock cases
+ case 0: case 1:
+ case 2: preScalar = 0; scalar = 0; dbl = 1; break;
+ case 3: preScalar = 1; scalar = 0; dbl = 1; break;
+ case 5: preScalar = 2; scalar = 0; dbl = 1; break;
+ case 7: preScalar = 3; scalar = 0; dbl = 1; break;
+
+ // Handle the scalar value 6 cases (since it's not a power of two, it won't get caught
+ // below)
+ case 9: preScalar = 1; scalar = 2; dbl = 1; break;
+ case 18: case 19: preScalar = 1; scalar = 2; dbl = 0; break;
+
+ case 15: preScalar = 2; scalar = 2; dbl = 1; break;
+ case 30: case 31: preScalar = 2; scalar = 2; dbl = 0; break;
+
+ case 21: case 22: case 23: preScalar = 3; scalar = 2; dbl = 1; break;
+ case 42: case 43: case 44: case 45: case 46: case 47: preScalar = 3; scalar = 2; dbl = 0; break;
+ default: {
+ int p2 = BitWork<VAL/2, 15>::highestBit();
+ int p3 = BitWork<VAL/3, 15>::highestBit();
+ int p5 = BitWork<VAL/5, 15>::highestBit();
+ int p7 = BitWork<VAL/7, 15>::highestBit();
+
+ int w2 = 2 * (1 << p2);
+ int w3 = (VAL/3) > 0 ? 3 * (1 << p3) : 0;
+ int w5 = (VAL/5) > 0 ? 5 * (1 << p5) : 0;
+ int w7 = (VAL/7) > 0 ? 7 * (1 << p7) : 0;
+
+ int maxval = MAX(MAX(w2, w3), MAX(w5, w7));
+
+ if(w2 == maxval) { preScalar = 0; scalar = p2; }
+ else if(w3 == maxval) { preScalar = 1; scalar = p3; }
+ else if(w5 == maxval) { preScalar = 2; scalar = p5; }
+ else if(w7 == maxval) { preScalar = 3; scalar = p7; }
+
+ dbl = 0;
+ if(scalar == 0) { dbl = 1; }
+ else if(scalar < 3) { scalar--; }
+ }
+ }
return;
}
@@ -84,114 +97,164 @@ template <int VAL> void getScalars(uint32_t & preScalar, uint32_t & scalar, uint
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX>
class ARMHardwareSPIOutput {
Selectable *m_pSelect;
+ SPIState gState;
- // Borrowed from the teensy3 SPSR emulation code
- static inline void enable_pins(void) __attribute__((always_inline)) {
+ // Borrowed from the teensy3 SPSR emulation code -- note, enabling pin 7 disables pin 11 (and vice versa),
+ // and likewise enabling pin 14 disables pin 13 (and vice versa)
+ inline void enable_pins(void) __attribute__((always_inline)) {
//serial_print("enable_pins\n");
- if(_DATA_PIN == 11) {
- CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
- CORE_PIN12_CONFIG = PORT_PCR_MUX(2);
- CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
- } else if(_DATA_PIN == 7) {
- CORE_PIN7_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
- CORE_PIN8_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
- CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
+ switch(_DATA_PIN) {
+ case 7:
+ CORE_PIN7_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
+ CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
+ break;
+ case 11:
+ CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
+ CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
+ break;
+ }
+
+ switch(_CLOCK_PIN) {
+ case 13:
+ CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
+ CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
+ break;
+ case 14:
+ CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
+ CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
+ break;
}
}
- // Borrowed from the teensy3 SPSR emulation code
- static inline void disable_pins(void) __attribute__((always_inline)) {
- //serial_print("disable_pins\n");
- if(_DATA_PIN == 11) {
- CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
- CORE_PIN12_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
- CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
- } else if(_DATA_PIN == 7) {
- CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
- CORE_PIN8_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
- CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
+ // Borrowed from the teensy3 SPSR emulation code. We disable the pins that we're using, and restore the state on the pins that we aren't using
+ inline void disable_pins(void) __attribute__((always_inline)) {
+ switch(_DATA_PIN) {
+ case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN11_CONFIG = gState.pins[1]; break;
+ case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN7_CONFIG = gState.pins[0]; break;
}
-}
-public:
- ARMHardwareSPIOutput() { m_pSelect = NULL; }
- ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
- void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
- static inline void update_ctar0(uint32_t ctar) __attribute__((always_inline)) {
- if (SPIX.CTAR0 == ctar) return;
- uint32_t mcr = SPIX.MCR;
- if (mcr & SPI_MCR_MDIS) {
- SPIX.CTAR0 = ctar;
- } else {
- SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
- SPIX.CTAR0 = ctar;
-
- SPIX.MCR = mcr;
- }
- }
-
- static inline void update_ctar1(uint32_t ctar) __attribute__((always_inline)) {
- if (SPIX.CTAR1 == ctar) return;
- uint32_t mcr = SPIX.MCR;
- if (mcr & SPI_MCR_MDIS) {
- SPIX.CTAR1 = ctar;
- } else {
- SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
- SPIX.CTAR1 = ctar;
- SPIX.MCR = mcr;
-
- }
- }
-
- void setSPIRate() {
+ switch(_CLOCK_PIN) {
+ case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN14_CONFIG = gState.pins[3]; break;
+ case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN13_CONFIG = gState.pins[2]; break;
+ }
+ }
+
+ static inline void update_ctars(uint32_t ctar0, uint32_t ctar1) __attribute__((always_inline)) {
+ if(SPIX.CTAR0 == ctar0 && SPIX.CTAR1 == ctar1) return;
+ uint32_t mcr = SPIX.MCR;
+ if(mcr & SPI_MCR_MDIS) {
+ SPIX.CTAR0 = ctar0;
+ SPIX.CTAR1 = ctar1;
+ } else {
+ SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
+ SPIX.CTAR0 = ctar0;
+ SPIX.CTAR1 = ctar1;
+ SPIX.MCR = mcr;
+ }
+ }
+
+ static inline void update_ctar0(uint32_t ctar) __attribute__((always_inline)) {
+ if (SPIX.CTAR0 == ctar) return;
+ uint32_t mcr = SPIX.MCR;
+ if (mcr & SPI_MCR_MDIS) {
+ SPIX.CTAR0 = ctar;
+ } else {
+ SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
+ SPIX.CTAR0 = ctar;
+
+ SPIX.MCR = mcr;
+ }
+ }
+
+ static inline void update_ctar1(uint32_t ctar) __attribute__((always_inline)) {
+ if (SPIX.CTAR1 == ctar) return;
+ uint32_t mcr = SPIX.MCR;
+ if (mcr & SPI_MCR_MDIS) {
+ SPIX.CTAR1 = ctar;
+ } else {
+ SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
+ SPIX.CTAR1 = ctar;
+ SPIX.MCR = mcr;
+
+ }
+ }
+
+ void setSPIRate() {
// Configure CTAR0, defaulting to 8 bits and CTAR1, defaulting to 16 bits
- uint32_t _PBR = 0;
- uint32_t _BR = 0;
- uint32_t _CSSCK = 0;
- uint32_t _DBR = 0;
-
- // if(_SPI_CLOCK_DIVIDER >= 256) { _PBR = 0; _BR = _CSSCK = 7; _DBR = 0; } // osc/256
- // else if(_SPI_CLOCK_DIVIDER >= 128) { _PBR = 0; _BR = _CSSCK = 6; _DBR = 0; } // osc/128
- // else if(_SPI_CLOCK_DIVIDER >= 64) { _PBR = 0; _BR = _CSSCK = 5; _DBR = 0; } // osc/64
- // else if(_SPI_CLOCK_DIVIDER >= 32) { _PBR = 0; _BR = _CSSCK = 4; _DBR = 0; } // osc/32
- // else if(_SPI_CLOCK_DIVIDER >= 16) { _PBR = 0; _BR = _CSSCK = 3; _DBR = 0; } // osc/16
- // else if(_SPI_CLOCK_DIVIDER >= 8) { _PBR = 0; _BR = _CSSCK = 1; _DBR = 0; } // osc/8
- // else if(_SPI_CLOCK_DIVIDER >= 7) { _PBR = 3; _BR = _CSSCK = 0; _DBR = 1; } // osc/7
- // else if(_SPI_CLOCK_DIVIDER >= 5) { _PBR = 2; _BR = _CSSCK = 0; _DBR = 1; } // osc/5
- // else if(_SPI_CLOCK_DIVIDER >= 4) { _PBR = 0; _BR = _CSSCK = 0; _DBR = 0; } // osc/4
- // else if(_SPI_CLOCK_DIVIDER >= 3) { _PBR = 1; _BR = _CSSCK = 0; _DBR = 1; } // osc/3
- // else { _PBR = 0; _BR = _CSSCK = 0; _DBR = 1; } // osc/2
-
- getScalars<_SPI_CLOCK_DIVIDER>(_PBR, _BR, _DBR);
- _CSSCK = _BR;
-
- uint32_t ctar0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
- uint32_t ctar1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
-
-#if USE_CONT == 1
- ctar0 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
- ctar1 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
-#endif
+ uint32_t _PBR = 0;
+ uint32_t _BR = 0;
+ uint32_t _CSSCK = 0;
+ uint32_t _DBR = 0;
+
+ // if(_SPI_CLOCK_DIVIDER >= 256) { _PBR = 0; _BR = _CSSCK = 7; _DBR = 0; } // osc/256
+ // else if(_SPI_CLOCK_DIVIDER >= 128) { _PBR = 0; _BR = _CSSCK = 6; _DBR = 0; } // osc/128
+ // else if(_SPI_CLOCK_DIVIDER >= 64) { _PBR = 0; _BR = _CSSCK = 5; _DBR = 0; } // osc/64
+ // else if(_SPI_CLOCK_DIVIDER >= 32) { _PBR = 0; _BR = _CSSCK = 4; _DBR = 0; } // osc/32
+ // else if(_SPI_CLOCK_DIVIDER >= 16) { _PBR = 0; _BR = _CSSCK = 3; _DBR = 0; } // osc/16
+ // else if(_SPI_CLOCK_DIVIDER >= 8) { _PBR = 0; _BR = _CSSCK = 1; _DBR = 0; } // osc/8
+ // else if(_SPI_CLOCK_DIVIDER >= 7) { _PBR = 3; _BR = _CSSCK = 0; _DBR = 1; } // osc/7
+ // else if(_SPI_CLOCK_DIVIDER >= 5) { _PBR = 2; _BR = _CSSCK = 0; _DBR = 1; } // osc/5
+ // else if(_SPI_CLOCK_DIVIDER >= 4) { _PBR = 0; _BR = _CSSCK = 0; _DBR = 0; } // osc/4
+ // else if(_SPI_CLOCK_DIVIDER >= 3) { _PBR = 1; _BR = _CSSCK = 0; _DBR = 1; } // osc/3
+ // else { _PBR = 0; _BR = _CSSCK = 0; _DBR = 1; } // osc/2
+
+ getScalars<_SPI_CLOCK_DIVIDER>(_PBR, _BR, _DBR);
+ _CSSCK = _BR;
+
+ uint32_t ctar0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
+ uint32_t ctar1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
+
+ #if USE_CONT == 1
+ ctar0 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
+ ctar1 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
+ #endif
+
+ if(_DBR) {
+ ctar0 |= SPI_CTAR_DBR;
+ ctar1 |= SPI_CTAR_DBR;
+ }
+
+ update_ctars(ctar0,ctar1);
+ }
- if(_DBR) {
- ctar0 |= SPI_CTAR_DBR;
- ctar1 |= SPI_CTAR_DBR;
- }
+ void inline save_spi_state() __attribute__ ((always_inline)) {
+ // save ctar data
+ gState._ctar0 = SPIX.CTAR0;
+ gState._ctar1 = SPIX.CTAR1;
- update_ctar0(ctar0);
- update_ctar1(ctar1);
+ // save data for the not-us pins
+ gState.pins[0] = CORE_PIN7_CONFIG;
+ gState.pins[1] = CORE_PIN11_CONFIG;
+ gState.pins[2] = CORE_PIN13_CONFIG;
+ gState.pins[3] = CORE_PIN14_CONFIG;
+ }
+
+ void inline restore_spi_state() __attribute__ ((always_inline)) {
+ // restore ctar data
+ update_ctars(gState._ctar0,gState._ctar1);
+
+ // restore data for the not-us pins (not necessary because disable_pins will do this)
+ // CORE_PIN7_CONFIG = gState.pins[0];
+ // CORE_PIN11_CONFIG = gState.pins[1];
+ // CORE_PIN13_CONFIG = gState.pins[2];
+ // CORE_PIN14_CONFIG = gState.pins[3];
+ }
+
+
+public:
+ ARMHardwareSPIOutput() { m_pSelect = NULL; }
+ ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
+ void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
- }
void init() {
// set the pins to output
FastPin<_DATA_PIN>::setOutput();
FastPin<_CLOCK_PIN>::setOutput();
- release();
// Enable SPI0 clock
uint32_t sim6 = SIM_SCGC6;
- if((SPI_t*)pSPIX == &SPI0) {
+ if((SPI_t*)pSPIX == &KINETISK_SPI0) {
if (!(sim6 & SIM_SCGC6_SPI0)) {
//serial_print("init1\n");
SIM_SCGC6 = sim6 | SIM_SCGC6_SPI0;
@@ -204,13 +267,12 @@ public:
SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1);
}
}
- setSPIRate();
// Configure SPI as the master and enable
SPIX.MCR |= SPI_MCR_MSTR; // | SPI_MCR_CONT_SCKE);
SPIX.MCR &= ~(SPI_MCR_MDIS | SPI_MCR_HALT);
- enable_pins();
+ // pin/spi configuration happens on select
}
static void waitFully() __attribute__((always_inline)) {
@@ -227,11 +289,11 @@ public:
enum EWait { PRE, POST, NONE };
enum ELast { NOTLAST, LAST };
-#if USE_CONT == 1
+ #if USE_CONT == 1
#define CM CONT
-#else
+ #else
#define CM NOCONT
-#endif
+ #endif
#define WM PRE
template<ECont CONT_STATE, EWait WAIT_STATE, ELast LAST_STATE> class Write {
@@ -239,16 +301,16 @@ public:
static void writeWord(uint16_t w) __attribute__((always_inline)) {
if(WAIT_STATE == PRE) { wait(); }
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
- ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
- SPI_PUSHR_CTAS(1) | (w & 0xFFFF);
+ ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
+ SPI_PUSHR_CTAS(1) | (w & 0xFFFF);
if(WAIT_STATE == POST) { wait(); }
}
static void writeByte(uint8_t b) __attribute__((always_inline)) {
if(WAIT_STATE == PRE) { wait(); }
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
- ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
- SPI_PUSHR_CTAS(0) | (b & 0xFF);
+ ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
+ SPI_PUSHR_CTAS(0) | (b & 0xFF);
if(WAIT_STATE == POST) { wait(); }
}
};
@@ -280,15 +342,24 @@ public:
update_ctar1(ctar1_save);
}
- void inline select() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->select(); } }
- void inline release() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->release(); } }
+ void inline select() __attribute__((always_inline)) {
+ save_spi_state();
+ if(m_pSelect != NULL) { m_pSelect->select(); }
+ setSPIRate();
+ enable_pins();
+ }
+
+ void inline release() __attribute__((always_inline)) {
+ disable_pins();
+ if(m_pSelect != NULL) { m_pSelect->release(); }
+ restore_spi_state();
+ }
static void writeBytesValueRaw(uint8_t value, int len) {
while(len--) { Write<CM, WM, NOTLAST>::writeByte(value); }
}
void writeBytesValue(uint8_t value, int len) {
- setSPIRate();
select();
while(len--) {
writeByte(value);
@@ -299,7 +370,6 @@ public:
// Write a block of n uint8_ts out
template <class D> void writeBytes(register uint8_t *data, int len) {
- setSPIRate();
uint8_t *end = data + len;
select();
// could be optimized to write 16bit words out instead of 8bit bytes
@@ -316,7 +386,6 @@ public:
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
- // setSPIRate();
select();
int len = pixels.mLen;
@@ -372,4 +441,6 @@ public:
};
#endif
+FASTLED_NAMESPACE_END
+
#endif
diff --git a/platforms/arm/k20/led_sysdefs_arm_k20.h b/platforms/arm/k20/led_sysdefs_arm_k20.h
new file mode 100644
index 00000000..0dcb626a
--- /dev/null
+++ b/platforms/arm/k20/led_sysdefs_arm_k20.h
@@ -0,0 +1,46 @@
+#ifndef __INC_LED_SYSDEFS_ARM_K20_H
+#define __INC_LED_SYSDEFS_ARM_K20_H
+
+#define FASTLED_TEENSY3
+#define FASTLED_ARM
+
+#ifndef INTERRUPT_THRESHOLD
+#define INTERRUPT_THRESHOLD 1
+#endif
+
+// Default to allowing interrupts
+#ifndef FASTLED_ALLOW_INTERRUPTS
+#define FASTLED_ALLOW_INTERRUPTS 1
+#endif
+
+#if FASTLED_ALLOW_INTERRUPTS == 1
+#define FASTLED_ACCURATE_CLOCK
+#endif
+
+#if (F_CPU == 96000000)
+#define CLK_DBL 1
+#endif
+
+// Get some system include files
+#include <avr/io.h>
+#include <avr/interrupt.h> // for cli/se definitions
+
+// Define the register types
+#if defined(ARDUINO) // && ARDUINO < 150
+typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
+typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
+#endif
+
+extern volatile uint32_t systick_millis_count;
+# define MS_COUNTER systick_millis_count
+
+
+// Default to using PROGMEM, since TEENSY3 provides it
+// even though all it does is ignore it. Just being
+// conservative here in case TEENSY3 changes.
+#ifndef FASTLED_USE_PROGMEM
+#define FASTLED_USE_PROGMEM 1
+#endif
+
+
+#endif
diff --git a/platforms/arm/k20/octows2811_controller.h b/platforms/arm/k20/octows2811_controller.h
new file mode 100644
index 00000000..ed748919
--- /dev/null
+++ b/platforms/arm/k20/octows2811_controller.h
@@ -0,0 +1,96 @@
+#ifndef __INC_OCTOWS2811_CONTROLLER_H
+#define __INC_OCTOWS2811_CONTROLLER_H
+
+#ifdef USE_OCTOWS2811
+
+// #include "OctoWS2811.h"
+
+FASTLED_NAMESPACE_BEGIN
+
+template<EOrder RGB_ORDER = GRB, boolean SLOW=false>
+class COctoWS2811Controller : public CLEDController {
+ OctoWS2811 *pocto;
+ uint8_t *drawbuffer,*framebuffer;
+
+ void _init(int nLeds) {
+ if(pocto == NULL) {
+ drawbuffer = (uint8_t*)malloc(nLeds * 8 * 3);
+ framebuffer = (uint8_t*)malloc(nLeds * 8 * 3);
+
+ // byte ordering is handled in show by the pixel controller
+ int config = WS2811_RGB;
+ if(SLOW) {
+ config |= WS2811_400kHz;
+ }
+
+ pocto = new OctoWS2811(nLeds, framebuffer, drawbuffer, config);
+
+ pocto->begin();
+ }
+ }
+public:
+ COctoWS2811Controller() { pocto = NULL; }
+
+
+ virtual void init() { /* do nothing yet */ }
+
+ virtual void clearLeds(int nLeds) {
+ _init(nLeds);
+ showColor(CRGB(0,0,0),nLeds,CRGB(0,0,0));
+ }
+
+ virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) {
+ _init(nLeds);
+ // Get our pixel values
+ PixelController<RGB_ORDER> pixels(data, nLeds, scale, getDither());
+ uint8_t ball[3][8];
+ memset(ball[0],pixels.loadAndScale0(),8);
+ memset(ball[1],pixels.loadAndScale1(),8);
+ memset(ball[2],pixels.loadAndScale2(),8);
+
+ uint8_t bout[24];
+ transpose8x1_MSB(ball[0],bout);
+ transpose8x1_MSB(ball[1],bout+8);
+ transpose8x1_MSB(ball[2],bout+16);
+
+ uint8_t *pdata = drawbuffer;
+ while(nLeds--) {
+ memcpy(pdata,bout,24);
+ pdata += 24;
+ }
+
+ pocto->show();
+ }
+
+ typedef union {
+ uint8_t bytes[8];
+ uint32_t raw[2];
+ } Lines;
+
+ virtual void show(const struct CRGB *rgbdata, int nLeds, CRGB scale) {
+ _init(nLeds);
+ MultiPixelController<8,0xFF,RGB_ORDER> pixels(rgbdata,nLeds, scale, getDither() );
+
+ uint8_t *pData = drawbuffer;
+ while(nLeds--) {
+ Lines b;
+
+ for(int i = 0; i < 8; i++) { b.bytes[i] = pixels.loadAndScale0(i); }
+ transpose8x1_MSB(b.bytes,pData); pData += 8;
+ for(int i = 0; i < 8; i++) { b.bytes[i] = pixels.loadAndScale1(i); }
+ transpose8x1_MSB(b.bytes,pData); pData += 8;
+ for(int i = 0; i < 8; i++) { b.bytes[i] = pixels.loadAndScale2(i); }
+ transpose8x1_MSB(b.bytes,pData); pData += 8;
+ pixels.stepDithering();
+ pixels.advanceData();
+ }
+
+ pocto->show();
+ }
+};
+
+FASTLED_NAMESPACE_END
+
+#endif
+
+#endif
diff --git a/smartmatrix_t3.h b/platforms/arm/k20/smartmatrix_t3.h
index ba77d149..06b04f18 100644
--- a/smartmatrix_t3.h
+++ b/platforms/arm/k20/smartmatrix_t3.h
@@ -4,6 +4,8 @@
#ifdef SmartMatrix_h
#include<SmartMatrix.h>
+FASTLED_NAMESPACE_BEGIN
+
extern SmartMatrix *pSmartMatrix;
// note - dmx simple must be included before FastSPI for this code to be enabled
@@ -74,6 +76,8 @@ public:
#endif
};
+FASTLED_NAMESPACE_END
+
#endif
#endif
diff --git a/platforms/arm/kl26/clockless_arm_kl26.h b/platforms/arm/kl26/clockless_arm_kl26.h
new file mode 100644
index 00000000..8c441a8e
--- /dev/null
+++ b/platforms/arm/kl26/clockless_arm_kl26.h
@@ -0,0 +1,89 @@
+#ifndef __INC_CLOCKLESS_ARM_KL26
+#define __INC_CLOCKLESS_ARM_KL26
+
+#include "platforms/arm/common/m0clockless.h"
+FASTLED_NAMESPACE_BEGIN
+#define FASTLED_HAS_CLOCKLESS 1
+
+template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
+class ClocklessController : public CLEDController {
+ typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
+ typedef typename FastPinBB<DATA_PIN>::port_t data_t;
+
+ data_t mPinMask;
+ data_ptr_t mPort;
+ CMinWait<WAIT_TIME> mWait;
+public:
+ virtual void init() {
+ FastPinBB<DATA_PIN>::setOutput();
+ mPinMask = FastPinBB<DATA_PIN>::mask();
+ mPort = FastPinBB<DATA_PIN>::port();
+ }
+
+ virtual uint16_t getMaxRefreshRate() const { return 400; }
+
+ virtual void clearLeds(int nLeds) {
+ showColor(CRGB(0, 0, 0), nLeds, 0);
+ }
+
+ // set all the leds on the controller to a given color
+ virtual void showColor(const struct CRGB & rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+ mWait.wait();
+ cli();
+
+ showRGBInternal(pixels);
+
+ sei();
+ mWait.mark();
+ }
+
+ virtual void show(const struct CRGB *rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+ mWait.wait();
+ cli();
+
+ showRGBInternal(pixels);
+
+ sei();
+ mWait.mark();
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void show(const struct CARGB *rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+ mWait.wait();
+ cli();
+ showRGBInternal(pixels);
+ sei();
+ mWait.mark();
+ }
+#endif
+
+ // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+ // gcc will use register Y for the this pointer.
+ static uint32_t showRGBInternal(PixelController<RGB_ORDER> & pixels) {
+ struct M0ClocklessData data;
+ data.d[0] = pixels.d[0];
+ data.d[1] = pixels.d[1];
+ data.d[2] = pixels.d[2];
+ data.s[0] = pixels.mScale[0];
+ data.s[1] = pixels.mScale[1];
+ data.s[2] = pixels.mScale[2];
+ data.e[0] = pixels.e[0];
+ data.e[1] = pixels.e[1];
+ data.e[2] = pixels.e[2];
+ data.adj = pixels.mAdvance;
+
+ typename FastPin<DATA_PIN>::port_ptr_t portBase = FastPin<DATA_PIN>::port();
+ showLedData<4,8,T1,T2,T3,RGB_ORDER, WAIT_TIME>(portBase, FastPin<DATA_PIN>::mask(), pixels.mData, pixels.mLen, &data);
+ return 0; // 0x00FFFFFF - _VAL;
+ }
+
+
+};
+
+FASTLED_NAMESPACE_END
+
+
+#endif // __INC_CLOCKLESS_ARM_D21
diff --git a/platforms/arm/kl26/fastled_arm_kl26.h b/platforms/arm/kl26/fastled_arm_kl26.h
new file mode 100644
index 00000000..9a93caa5
--- /dev/null
+++ b/platforms/arm/kl26/fastled_arm_kl26.h
@@ -0,0 +1,10 @@
+#ifndef __INC_FASTLED_ARM_KL26_H
+#define __INC_FASTLED_ARM_KL26_H
+
+// Include the k20 headers
+#include "fastled_delay.h"
+#include "fastpin_arm_kl26.h"
+#include "fastspi_arm_kl26.h"
+#include "clockless_arm_kl26.h"
+
+#endif
diff --git a/platforms/arm/kl26/fastpin_arm_kl26.h b/platforms/arm/kl26/fastpin_arm_kl26.h
new file mode 100644
index 00000000..4c30cd78
--- /dev/null
+++ b/platforms/arm/kl26/fastpin_arm_kl26.h
@@ -0,0 +1,88 @@
+#ifndef __FASTPIN_ARM_KL26_H
+#define __FASTPIN_ARM_KL26_H
+
+FASTLED_NAMESPACE_BEGIN
+
+#if defined(FASTLED_FORCE_SOFTWARE_PINS)
+#warning "Software pin support forced, pin access will be sloightly slower."
+#define NO_HARDWARE_PIN_SUPPORT
+#undef HAS_HARDWARE_PIN_SUPPORT
+
+#else
+
+
+/// Template definition for teensy LC style ARM pins, providing direct access to the various GPIO registers. Note that this
+/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
+/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
+/// The registers are data output, set output, clear output, toggle output, input, and direction
+template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN {
+public:
+ typedef volatile uint32_t * port_ptr_t;
+ typedef uint32_t port_t;
+
+ inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
+ inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
+
+ inline static void hi() __attribute__ ((always_inline)) { _PSOR::r() = _MASK; }
+ inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; }
+ inline static void set(register port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
+
+ inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
+
+ inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; }
+
+ inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
+ inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
+ inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
+
+ inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
+ inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
+ inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
+ inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
+ inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
+ inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
+};
+
+// Macros for kl26 pin access/definition
+#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
+#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))
+
+#define _R(T) struct __gen_struct_ ## T
+#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } \
+template<int BIT> static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
+#define _IO32(L) _RD32(FGPIO ## L ## _PDOR); _RD32(FGPIO ## L ## _PSOR); _RD32(FGPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(FGPIO ## L ## _PDIR); _RD32(FGPIO ## L ## _PDDR);
+
+#define _DEFPIN_ARM(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << BIT, _R(FGPIO ## L ## _PDOR), _R(FGPIO ## L ## _PSOR), _R(FGPIO ## L ## _PCOR), \
+_R(GPIO ## L ## _PTOR), _R(FGPIO ## L ## _PDIR), _R(FGPIO ## L ## _PDDR)> {}; \
+/* template<> class FastPinBB<PIN> : public _ARMPIN_BITBAND<PIN, BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
+_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {}; */
+
+// Actual pin definitions
+#if defined(FASTLED_TEENSYLC) && defined(CORE_TEENSY)
+
+_IO32(A); _IO32(B); _IO32(C); _IO32(D); _IO32(E);
+
+#define MAX_PIN 26
+_DEFPIN_ARM(0, 16, B); _DEFPIN_ARM(1, 17, B); _DEFPIN_ARM(2, 0, D); _DEFPIN_ARM(3, 1, A);
+_DEFPIN_ARM(4, 2, A); _DEFPIN_ARM(5, 7, D); _DEFPIN_ARM(6, 4, D); _DEFPIN_ARM(7, 2, D);
+_DEFPIN_ARM(8, 3, D); _DEFPIN_ARM(9, 3, C); _DEFPIN_ARM(10, 4, C); _DEFPIN_ARM(11, 6, C);
+_DEFPIN_ARM(12, 7, C); _DEFPIN_ARM(13, 5, C); _DEFPIN_ARM(14, 1, D); _DEFPIN_ARM(15, 0, C);
+_DEFPIN_ARM(16, 0, B); _DEFPIN_ARM(17, 1, B); _DEFPIN_ARM(18, 3, B); _DEFPIN_ARM(19, 2, B);
+_DEFPIN_ARM(20, 5, D); _DEFPIN_ARM(21, 6, D); _DEFPIN_ARM(22, 1, C); _DEFPIN_ARM(23, 2, C);
+_DEFPIN_ARM(24, 20, E); _DEFPIN_ARM(25, 21, E); _DEFPIN_ARM(26, 30, E);
+
+#define SPI_DATA 11
+#define SPI_CLOCK 13
+// #define SPI1 (*(SPI_t *)0x4002D000)
+
+#define SPI2_DATA 0
+#define SPI2_CLOCK 20
+
+#define HAS_HARDWARE_PIN_SUPPORT
+#endif
+
+#endif // FASTLED_FORCE_SOFTWARE_PINS
+
+FASTLED_NAMESPACE_END
+
+#endif // __INC_FASTPIN_ARM_K20
diff --git a/platforms/arm/kl26/fastspi_arm_kl26.h b/platforms/arm/kl26/fastspi_arm_kl26.h
new file mode 100644
index 00000000..869b6054
--- /dev/null
+++ b/platforms/arm/kl26/fastspi_arm_kl26.h
@@ -0,0 +1,252 @@
+#ifndef __INC_FASTSPI_ARM_KL26_H
+#define __INC_FASTSPI_ARM_KL26_h
+
+FASTLED_NAMESPACE_BEGIN
+
+template <int VAL> void getScalars(uint8_t & sppr, uint8_t & spr) {
+ if(VAL > 4096) { sppr=7; spr=8; }
+ else if(VAL > 3584) { sppr=6; spr=8; }
+ else if(VAL > 3072) { sppr=5; spr=8; }
+ else if(VAL > 2560) { sppr=4; spr=8; }
+ else if(VAL > 2048) { sppr=7; spr=7; }
+ else if(VAL > 2048) { sppr=3; spr=8; }
+ else if(VAL > 1792) { sppr=6; spr=7; }
+ else if(VAL > 1536) { sppr=5; spr=7; }
+ else if(VAL > 1536) { sppr=2; spr=8; }
+ else if(VAL > 1280) { sppr=4; spr=7; }
+ else if(VAL > 1024) { sppr=7; spr=6; }
+ else if(VAL > 1024) { sppr=3; spr=7; }
+ else if(VAL > 1024) { sppr=1; spr=8; }
+ else if(VAL > 896) { sppr=6; spr=6; }
+ else if(VAL > 768) { sppr=5; spr=6; }
+ else if(VAL > 768) { sppr=2; spr=7; }
+ else if(VAL > 640) { sppr=4; spr=6; }
+ else if(VAL > 512) { sppr=7; spr=5; }
+ else if(VAL > 512) { sppr=3; spr=6; }
+ else if(VAL > 512) { sppr=1; spr=7; }
+ else if(VAL > 512) { sppr=0; spr=8; }
+ else if(VAL > 448) { sppr=6; spr=5; }
+ else if(VAL > 384) { sppr=5; spr=5; }
+ else if(VAL > 384) { sppr=2; spr=6; }
+ else if(VAL > 320) { sppr=4; spr=5; }
+ else if(VAL > 256) { sppr=7; spr=4; }
+ else if(VAL > 256) { sppr=3; spr=5; }
+ else if(VAL > 256) { sppr=1; spr=6; }
+ else if(VAL > 256) { sppr=0; spr=7; }
+ else if(VAL > 224) { sppr=6; spr=4; }
+ else if(VAL > 192) { sppr=5; spr=4; }
+ else if(VAL > 192) { sppr=2; spr=5; }
+ else if(VAL > 160) { sppr=4; spr=4; }
+ else if(VAL > 128) { sppr=7; spr=3; }
+ else if(VAL > 128) { sppr=3; spr=4; }
+ else if(VAL > 128) { sppr=1; spr=5; }
+ else if(VAL > 128) { sppr=0; spr=6; }
+ else if(VAL > 112) { sppr=6; spr=3; }
+ else if(VAL > 96) { sppr=5; spr=3; }
+ else if(VAL > 96) { sppr=2; spr=4; }
+ else if(VAL > 80) { sppr=4; spr=3; }
+ else if(VAL > 64) { sppr=7; spr=2; }
+ else if(VAL > 64) { sppr=3; spr=3; }
+ else if(VAL > 64) { sppr=1; spr=4; }
+ else if(VAL > 64) { sppr=0; spr=5; }
+ else if(VAL > 56) { sppr=6; spr=2; }
+ else if(VAL > 48) { sppr=5; spr=2; }
+ else if(VAL > 48) { sppr=2; spr=3; }
+ else if(VAL > 40) { sppr=4; spr=2; }
+ else if(VAL > 32) { sppr=7; spr=1; }
+ else if(VAL > 32) { sppr=3; spr=2; }
+ else if(VAL > 32) { sppr=1; spr=3; }
+ else if(VAL > 32) { sppr=0; spr=4; }
+ else if(VAL > 28) { sppr=6; spr=1; }
+ else if(VAL > 24) { sppr=5; spr=1; }
+ else if(VAL > 24) { sppr=2; spr=2; }
+ else if(VAL > 20) { sppr=4; spr=1; }
+ else if(VAL > 16) { sppr=7; spr=0; }
+ else if(VAL > 16) { sppr=3; spr=1; }
+ else if(VAL > 16) { sppr=1; spr=2; }
+ else if(VAL > 16) { sppr=0; spr=3; }
+ else if(VAL > 14) { sppr=6; spr=0; }
+ else if(VAL > 12) { sppr=5; spr=0; }
+ else if(VAL > 12) { sppr=2; spr=1; }
+ else if(VAL > 10) { sppr=4; spr=0; }
+ else if(VAL > 8) { sppr=3; spr=0; }
+ else if(VAL > 8) { sppr=1; spr=1; }
+ else if(VAL > 8) { sppr=0; spr=2; }
+ else if(VAL > 6) { sppr=2; spr=0; }
+ else if(VAL > 4) { sppr=1; spr=0; }
+ else if(VAL > 4) { sppr=0; spr=1; }
+ else /* if(VAL > 2) */ { sppr=0; spr=0; }
+}
+
+
+#define SPIX (*(KINETISL_SPI_t*)pSPIX)
+#define ARM_HARDWARE_SPI
+
+template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX>
+class ARMHardwareSPIOutput {
+ Selectable *m_pSelect;
+
+ static inline void enable_pins(void) __attribute__((always_inline)) {
+ switch(_DATA_PIN) {
+ case 0: CORE_PIN0_CONFIG = PORT_PCR_MUX(2); break;
+ case 1: CORE_PIN1_CONFIG = PORT_PCR_MUX(5); break;
+ case 7: CORE_PIN7_CONFIG = PORT_PCR_MUX(2); break;
+ case 8: CORE_PIN8_CONFIG = PORT_PCR_MUX(5); break;
+ case 11: CORE_PIN11_CONFIG = PORT_PCR_MUX(2); break;
+ case 12: CORE_PIN12_CONFIG = PORT_PCR_MUX(5); break;
+ case 21: CORE_PIN21_CONFIG = PORT_PCR_MUX(2); break;
+ }
+
+ switch(_CLOCK_PIN) {
+ case 13: CORE_PIN13_CONFIG = PORT_PCR_MUX(2); break;
+ case 14: CORE_PIN14_CONFIG = PORT_PCR_MUX(2); break;
+ case 20: CORE_PIN20_CONFIG = PORT_PCR_MUX(2); break;
+ }
+ }
+
+ static inline void disable_pins(void) __attribute((always_inline)) {
+ switch(_DATA_PIN) {
+ case 0: CORE_PIN0_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
+ case 1: CORE_PIN1_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
+ case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
+ case 8: CORE_PIN8_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
+ case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
+ case 12: CORE_PIN12_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
+ case 21: CORE_PIN21_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
+ }
+
+ switch(_CLOCK_PIN) {
+ case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
+ case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
+ case 20: CORE_PIN20_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
+ }
+ }
+
+ void setSPIRate() {
+ uint8_t sppr, spr;
+ getScalars<_SPI_CLOCK_DIVIDER>(sppr, spr);
+
+ // Set the speed
+ SPIX.BR = SPI_BR_SPPR(sppr) | SPI_BR_SPR(spr);
+
+ // Also, force 8 bit transfers (don't want to juggle 8/16 since that flushes the world)
+ SPIX.C2 = 0;
+ SPIX.C1 |= SPI_C1_SPE;
+ }
+
+public:
+ ARMHardwareSPIOutput() { m_pSelect = NULL; }
+ ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
+
+ // set the object representing the selectable
+ void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
+
+ // initialize the SPI subssytem
+ void init() {
+ FastPin<_DATA_PIN>::setOutput();
+ FastPin<_CLOCK_PIN>::setOutput();
+
+ // Enable the SPI clocks
+ uint32_t sim4 = SIM_SCGC4;
+ if ((pSPIX == 0x40076000) && !(sim4 & SIM_SCGC4_SPI0)) {
+ SIM_SCGC4 = sim4 | SIM_SCGC4_SPI0;
+ }
+
+ if ( (pSPIX == 0x40077000) && !(sim4 & SIM_SCGC4_SPI1)) {
+ SIM_SCGC4 = sim4 | SIM_SCGC4_SPI1;
+ }
+
+ SPIX.C1 = SPI_C1_MSTR | SPI_C1_SPE;
+ SPIX.C2 = 0;
+ SPIX.BR = SPI_BR_SPPR(1) | SPI_BR_SPR(0);
+ }
+
+ // latch the CS select
+ void inline select() __attribute__((always_inline)) {
+ if(m_pSelect != NULL) { m_pSelect->select(); }
+ setSPIRate();
+ enable_pins();
+ }
+
+
+ // release the CS select
+ void inline release() __attribute__((always_inline)) {
+ disable_pins();
+ if(m_pSelect != NULL) { m_pSelect->release(); }
+ }
+
+ // Wait for the world to be clear
+ static void wait() __attribute__((always_inline)) { while(!(SPIX.S & SPI_S_SPTEF)); }
+
+ // wait until all queued up data has been written
+ void waitFully() { wait(); }
+
+ // not the most efficient mechanism in the world - but should be enough for sm16716 and friends
+ template <uint8_t BIT> inline static void writeBit(uint8_t b) { /* TODO */ }
+
+ // write a byte out via SPI (returns immediately on writing register)
+ static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.DL = b; }
+ // write a word out via SPI (returns immediately on writing register)
+ static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w & 0xFF); }
+
+ // A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
+ static void writeBytesValueRaw(uint8_t value, int len) {
+ while(len--) { writeByte(value); }
+ }
+
+ // A full cycle of writing a value for len bytes, including select, release, and waiting
+ void writeBytesValue(uint8_t value, int len) {
+ setSPIRate();
+ select();
+ while(len--) {
+ writeByte(value);
+ }
+ waitFully();
+ release();
+ }
+
+ // A full cycle of writing a raw block of data out, including select, release, and waiting
+ template <class D> void writeBytes(register uint8_t *data, int len) {
+ setSPIRate();
+ uint8_t *end = data + len;
+ select();
+ // could be optimized to write 16bit words out instead of 8bit bytes
+ while(data != end) {
+ writeByte(D::adjust(*data++));
+ }
+ D::postBlock(len);
+ waitFully();
+ release();
+ }
+
+ void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
+
+
+ template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
+ int len = pixels.mLen;
+
+ select();
+ while(pixels.has(1)) {
+ if(FLAGS & FLAG_START_BIT) {
+ writeBit<0>(1);
+ writeByte(D::adjust(pixels.loadAndScale0()));
+ writeByte(D::adjust(pixels.loadAndScale1()));
+ writeByte(D::adjust(pixels.loadAndScale2()));
+ } else {
+ writeByte(D::adjust(pixels.loadAndScale0()));
+ writeByte(D::adjust(pixels.loadAndScale1()));
+ writeByte(D::adjust(pixels.loadAndScale2()));
+ }
+
+ pixels.advanceData();
+ pixels.stepDithering();
+ }
+ D::postBlock(len);
+ release();
+ }
+
+};
+
+FASTLED_NAMESPACE_END
+
+#endif
diff --git a/platforms/arm/kl26/led_sysdefs_arm_kl26.h b/platforms/arm/kl26/led_sysdefs_arm_kl26.h
new file mode 100644
index 00000000..5dca7f11
--- /dev/null
+++ b/platforms/arm/kl26/led_sysdefs_arm_kl26.h
@@ -0,0 +1,45 @@
+#ifndef __INC_LED_SYSDEFS_ARM_KL26_H
+#define __INC_LED_SYSDEFS_ARM_KL26_H
+
+#define FASTLED_TEENSYLC
+#define FASTLED_ARM
+#define FASTLED_ARM_M0_PLUS
+
+#ifndef INTERRUPT_THRESHOLD
+#define INTERRUPT_THRESHOLD 1
+#endif
+
+// Default to allowing interrupts
+#ifndef FASTLED_ALLOW_INTERRUPTS
+#define FASTLED_ALLOW_INTERRUPTS 1
+#endif
+
+#if FASTLED_ALLOW_INTERRUPTS == 1
+#define FASTLED_ACCURATE_CLOCK
+#endif
+
+#if (F_CPU == 96000000)
+#define CLK_DBL 1
+#endif
+
+// Get some system include files
+#include <avr/io.h>
+#include <avr/interrupt.h> // for cli/se definitions
+
+// Define the register types
+#if defined(ARDUINO) // && ARDUINO < 150
+typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
+typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
+#endif
+
+extern volatile uint32_t systick_millis_count;
+# define MS_COUNTER systick_millis_count
+
+// Default to using PROGMEM since TEENSYLC provides it
+// even though all it does is ignore it. Just being
+// conservative here in case TEENSYLC changes.
+#ifndef FASTLED_USE_PROGMEM
+#define FASTLED_USE_PROGMEM 1
+#endif
+
+#endif
diff --git a/platforms/arm/nrf51/clockless_arm_nrf51.h b/platforms/arm/nrf51/clockless_arm_nrf51.h
new file mode 100644
index 00000000..98408ada
--- /dev/null
+++ b/platforms/arm/nrf51/clockless_arm_nrf51.h
@@ -0,0 +1,117 @@
+#ifndef __INC_CLOCKLESS_ARM_NRF51
+#define __INC_CLOCKLESS_ARM_NRF51
+
+#if defined(NRF51)
+
+#include "nrf51_bitfields.h"
+#define FASTLED_HAS_CLOCKLESS 1
+
+#if (FASTLED_ALLOW_INTERRUPTS==1)
+#define SEI_CHK LED_TIMER->CC[0] = (WAIT_TIME * (F_CPU/1000000)); LED_TIMER->TASKS_CLEAR; LED_TIMER->EVENTS_COMPARE[0] = 0;
+#define CLI_CHK cli(); if(LED_TIMER->EVENTS_COMPARE[0]) { LED_TIMER->TASKS_STOP = 1; return 0; }
+#define INNER_SEI sei();
+#else
+#define SEI_CHK
+#define CLI_CHK
+#define INNER_SEI delaycycles<1>();
+#endif
+
+
+#include "platforms/arm/common/m0clockless.h"
+template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 75>
+class ClocklessController : public CLEDController {
+ typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
+ typedef typename FastPinBB<DATA_PIN>::port_t data_t;
+
+ data_t mPinMask;
+ data_ptr_t mPort;
+ CMinWait<WAIT_TIME> mWait;
+public:
+ virtual void init() {
+ FastPinBB<DATA_PIN>::setOutput();
+ mPinMask = FastPinBB<DATA_PIN>::mask();
+ mPort = FastPinBB<DATA_PIN>::port();
+ }
+
+ virtual uint16_t getMaxRefreshRate() const { return 400; }
+
+ virtual void clearLeds(int nLeds) {
+ showColor(CRGB(0, 0, 0), nLeds, 0);
+ }
+
+ // set all the leds on the controller to a given color
+ virtual void showColor(const struct CRGB & rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+ mWait.wait();
+ cli();
+
+ // attempt to re-show a frame if we exit early because of interrupts.
+ if(!showRGBInternal(pixels)) {
+ sei(); delayMicroseconds(WAIT_TIME); cli();
+ showRGBInternal(pixels);
+ }
+
+ sei();
+ mWait.mark();
+ }
+
+ virtual void show(const struct CRGB *rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+ mWait.wait();
+ cli();
+
+ if(!showRGBInternal(pixels)) {
+ sei(); delayMicroseconds(WAIT_TIME); cli();
+ showRGBInternal(pixels);
+ }
+
+ sei();
+ mWait.mark();
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void show(const struct CARGB *rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+ mWait.wait();
+ cli();
+ showRGBInternal(pixels);
+ sei();
+ mWait.mark();
+ }
+#endif
+
+ // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+ // gcc will use register Y for the this pointer.
+ static uint32_t showRGBInternal(PixelController<RGB_ORDER> & pixels) {
+ struct M0ClocklessData data;
+ data.d[0] = pixels.d[0];
+ data.d[1] = pixels.d[1];
+ data.d[2] = pixels.d[2];
+ data.s[0] = pixels.mScale[0];
+ data.s[1] = pixels.mScale[1];
+ data.s[2] = pixels.mScale[2];
+ data.e[0] = pixels.e[0];
+ data.e[1] = pixels.e[1];
+ data.e[2] = pixels.e[2];
+ data.adj = pixels.mAdvance;
+
+ typename FastPin<DATA_PIN>::port_ptr_t portBase = FastPin<DATA_PIN>::port();
+
+ // timer mode w/prescaler of 0
+ LED_TIMER->MODE = TIMER_MODE_MODE_Timer;
+ LED_TIMER->PRESCALER = 0;
+ LED_TIMER->EVENTS_COMPARE[0] = 0;
+ LED_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
+ LED_TIMER->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk;
+ LED_TIMER->TASKS_START = 1;
+
+ int ret = showLedData<4,8,T1,T2,T3,RGB_ORDER,WAIT_TIME>(portBase, FastPin<DATA_PIN>::mask(), pixels.mData, pixels.mLen, &data);
+
+ LED_TIMER->TASKS_STOP = 1;
+ return ret; // 0x00FFFFFF - _VAL;
+ }
+};
+
+
+#endif // NRF51
+#endif // __INC_CLOCKLESS_ARM_NRF51
diff --git a/platforms/arm/nrf51/fastled_arm_nrf51.h b/platforms/arm/nrf51/fastled_arm_nrf51.h
new file mode 100644
index 00000000..bfdefc34
--- /dev/null
+++ b/platforms/arm/nrf51/fastled_arm_nrf51.h
@@ -0,0 +1,11 @@
+#ifndef __INC_FASTLED_ARM_NRF51_H
+#define __INC_FASTLED_ARM_NRF51_H
+
+// Include the k20 headers
+#include "bitswap.h"
+#include "fastled_delay.h"
+#include "fastpin_arm_nrf51.h"
+#include "fastspi_arm_nrf51.h"
+#include "clockless_arm_nrf51.h"
+
+#endif
diff --git a/platforms/arm/nrf51/fastpin_arm_nrf51.h b/platforms/arm/nrf51/fastpin_arm_nrf51.h
new file mode 100644
index 00000000..4125f9a3
--- /dev/null
+++ b/platforms/arm/nrf51/fastpin_arm_nrf51.h
@@ -0,0 +1,119 @@
+#ifndef __FASTPIN_ARM_NRF51_H
+#define __FASTPIN_ARM_NRF51_H
+
+#if defined(NRF51)
+/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
+/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
+/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
+/// The registers are data output, set output, clear output, toggle output, input, and direction
+#if 0
+template<uint8_t PIN, uint32_t _MASK, typename _DIRSET, typename _DIRCLR, typename _OUTSET, typename _OUTCLR, typename _OUT> class _ARMPIN {
+public:
+ typedef volatile uint32_t * port_ptr_t;
+ typedef uint32_t port_t;
+
+ inline static void setOutput() { _DIRSET::r() = _MASK; }
+ inline static void setInput() { _DIRCLR::r() = _MASK; }
+
+ inline static void hi() __attribute__ ((always_inline)) { _OUTSET::r() = _MASK; }
+ inline static void lo() __attribute__ ((always_inline)) { _OUTCLR::r() = _MASK; }
+ inline static void set(register port_t val) __attribute__ ((always_inline)) { _OUT::r() = val; }
+
+ inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
+
+ inline static void toggle() __attribute__ ((always_inline)) { _OUT::r() ^= _MASK; }
+
+ inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
+ inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
+ inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
+
+ inline static port_t hival() __attribute__ ((always_inline)) { return _OUT::r() | _MASK; }
+ inline static port_t loval() __attribute__ ((always_inline)) { return _OUT::r() & ~_MASK; }
+ inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_OUT::r(); }
+ inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
+};
+
+#define ADDR(X) *(volatile uint32_t*)X
+#define NR_GPIO_ADDR(base,offset) (*(volatile uint32_t *))((uint32_t)(base + offset))
+#define NR_DIRSET ADDR(0x50000518UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x518)
+#define NR_DIRCLR ADDR(0x5000051CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x51C)
+#define NR_OUTSET ADDR(0x50000508UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x508)
+#define NR_OUTCLR ADDR(0x5000050CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x50C)
+#define NR_OUT ADDR(0x50000504UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x504)
+
+#define _RD32_NRF(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; }};
+
+_RD32_NRF(NR_DIRSET);
+_RD32_NRF(NR_DIRCLR);
+_RD32_NRF(NR_OUTSET);
+_RD32_NRF(NR_OUTCLR);
+_RD32_NRF(NR_OUT);
+
+#define _DEFPIN_ARM(PIN) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << PIN, \
+ _R(NR_DIRSET), _R(NR_DIRCLR), _R(NR_OUTSET), _R(NR_OUTCLR), _R(NR_OUT)> {};
+#else
+
+typedef struct { /*!< GPIO Structure */
+ // __I uint32_t RESERVED0[321];
+ __IO uint32_t OUT; /*!< Write GPIO port. */
+ __IO uint32_t OUTSET; /*!< Set individual bits in GPIO port. */
+ __IO uint32_t OUTCLR; /*!< Clear individual bits in GPIO port. */
+ __I uint32_t IN; /*!< Read GPIO port. */
+ __IO uint32_t DIR; /*!< Direction of GPIO pins. */
+ __IO uint32_t DIRSET; /*!< DIR set register. */
+ __IO uint32_t DIRCLR; /*!< DIR clear register. */
+ __I uint32_t RESERVED1[120];
+ __IO uint32_t PIN_CNF[32]; /*!< Configuration of GPIO pins. */
+} FL_NRF_GPIO_Type;
+
+#define FL_NRF_GPIO_BASE 0x50000504UL
+#define FL_NRF_GPIO ((FL_NRF_GPIO_Type *) FL_NRF_GPIO_BASE)
+
+template<uint8_t PIN, uint32_t _MASK> class _ARMPIN {
+public:
+ typedef volatile uint32_t * port_ptr_t;
+ typedef uint32_t port_t;
+
+ inline static void setOutput() { FL_NRF_GPIO->DIRSET = _MASK; }
+ inline static void setInput() { FL_NRF_GPIO->DIRCLR = _MASK; }
+
+ inline static void hi() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTSET = _MASK; }
+ inline static void lo() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTCLR= _MASK; }
+ inline static void set(register port_t val) __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT = val; }
+
+ inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
+
+ inline static void toggle() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT ^= _MASK; }
+
+ inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
+ inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
+ inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
+
+ inline static port_t hival() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT | _MASK; }
+ inline static port_t loval() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT & ~_MASK; }
+ inline static port_ptr_t port() __attribute__ ((always_inline)) { return &FL_NRF_GPIO->OUT; }
+ inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
+
+ inline static bool isset() __attribute__ ((always_inline)) { return (FL_NRF_GPIO->IN & _MASK) != 0; }
+};
+
+
+#define _DEFPIN_ARM(PIN) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << PIN> {};
+#endif
+
+// Actual pin definitions
+#define MAX_PIN 31
+_DEFPIN_ARM(0); _DEFPIN_ARM(1); _DEFPIN_ARM(2); _DEFPIN_ARM(3);
+_DEFPIN_ARM(4); _DEFPIN_ARM(5); _DEFPIN_ARM(6); _DEFPIN_ARM(7);
+_DEFPIN_ARM(8); _DEFPIN_ARM(9); _DEFPIN_ARM(10); _DEFPIN_ARM(11);
+_DEFPIN_ARM(12); _DEFPIN_ARM(13); _DEFPIN_ARM(14); _DEFPIN_ARM(15);
+_DEFPIN_ARM(16); _DEFPIN_ARM(17); _DEFPIN_ARM(18); _DEFPIN_ARM(19);
+_DEFPIN_ARM(20); _DEFPIN_ARM(21); _DEFPIN_ARM(22); _DEFPIN_ARM(23);
+_DEFPIN_ARM(24); _DEFPIN_ARM(25); _DEFPIN_ARM(26); _DEFPIN_ARM(27);
+_DEFPIN_ARM(28); _DEFPIN_ARM(29); _DEFPIN_ARM(30); _DEFPIN_ARM(31);
+
+#define HAS_HARDWARE_PIN_SUPPORT
+
+#endif
+
+#endif
diff --git a/platforms/arm/nrf51/fastspi_arm_nrf51.h b/platforms/arm/nrf51/fastspi_arm_nrf51.h
new file mode 100644
index 00000000..da166db0
--- /dev/null
+++ b/platforms/arm/nrf51/fastspi_arm_nrf51.h
@@ -0,0 +1,146 @@
+#ifndef __INC_FASTSPI_NRF_H
+#define __INC_FASTSPI_NRF_H
+
+#ifdef NRF51
+
+// A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should
+// be used as a definition for the set of methods that the spi implementation classes should use (since C++ doesn't support the
+// idea of interfaces - it's possible this could be done with virtual classes, need to decide if i want that overhead)
+template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
+class NRF51SPIOutput {
+
+ struct saveData {
+ uint32_t sck;
+ uint32_t mosi;
+ uint32_t miso;
+ uint32_t freq;
+ uint32_t enable;
+ } mSavedData;
+
+ void saveSPIData() {
+ mSavedData.sck = NRF_SPI0->PSELSCK;
+ mSavedData.mosi = NRF_SPI0->PSELMOSI;
+ mSavedData.miso = NRF_SPI0->PSELMISO;
+ mSavedData.freq = NRF_SPI0->FREQUENCY;
+ mSavedData.enable = NRF_SPI0->ENABLE;
+ }
+
+ void restoreSPIData() {
+ NRF_SPI0->PSELSCK = mSavedData.sck;
+ NRF_SPI0->PSELMOSI = mSavedData.mosi;
+ NRF_SPI0->PSELMISO = mSavedData.miso;
+ NRF_SPI0->FREQUENCY = mSavedData.freq;
+ mSavedData.enable = NRF_SPI0->ENABLE;
+ }
+
+public:
+ NRF51SPIOutput() { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); }
+ NRF51SPIOutput(Selectable *pSelect) { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); }
+
+ // set the object representing the selectable
+ void setSelect(Selectable *pSelect) { /* TODO */ }
+
+ // initialize the SPI subssytem
+ void init() {
+ FastPin<_DATA_PIN>::setOutput();
+ FastPin<_CLOCK_PIN>::setOutput();
+ NRF_SPI0->PSELSCK = _CLOCK_PIN;
+ NRF_SPI0->PSELMOSI = _DATA_PIN;
+ NRF_SPI0->PSELMISO = 0xFFFFFFFF;
+ NRF_SPI0->FREQUENCY = 0x80000000;
+ NRF_SPI0->ENABLE = 1;
+ NRF_SPI0->EVENTS_READY = 0;
+ }
+
+ // latch the CS select
+ void select() { saveSPIData(); init(); }
+
+ // release the CS select
+ void release() { restoreSPIData(); }
+
+ static bool shouldWait(bool wait = false) __attribute__((always_inline)) __attribute__((always_inline)) {
+ static bool sWait=false;
+ return false; // if(sWait) { sWait = wait; return true; } else { sWait = wait; return false; }
+ }
+
+ // wait until all queued up data has been written
+ void waitFully() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->EVENTS_READY=0; uint8_t b = NRF_SPI0->RXD; }
+ void wait() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->EVENTS_READY=0; uint8_t b = NRF_SPI0->RXD; }
+ // void waitFully() { while(NRF_SPI0->EVENTS_READY==0); NRF_SPI0->EVENTS_READY=0; uint8_t b = NRF_SPI0->RXD; }
+ // void wait() { while(NRF_SPI0->EVENTS_READY==0); NRF_SPI0->EVENTS_READY=0; uint8_t b = NRF_SPI0->RXD; }
+
+ // write a byte out via SPI (returns immediately on writing register)
+ // void writeByte(uint8_t b) { wait(); NRF_SPI0->TXD = b; shouldWait(true); }
+ // void writeByte(uint8_t b) __attribute__((always_inline)){ wait(); NRF_SPI0->TXD = b; shouldWait(true); }
+ void writeByte(uint8_t b) __attribute__((always_inline)) { NRF_SPI0->EVENTS_READY=0; /*uint8_t x = NRF_SPI0->RXD;*/ NRF_SPI0->TXD = b; }
+
+ // write a word out via SPI (returns immediately on writing register)
+ void writeWord(uint16_t w) __attribute__((always_inline)){ writeByte(w>>8); writeByte(w & 0xFF); }
+
+ // A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
+ static void writeBytesValueRaw(uint8_t value, int len) { while(len--) { writeByte(value); } }
+
+ // A full cycle of writing a value for len bytes, including select, release, and waiting
+ void writeBytesValue(uint8_t value, int len) {
+ select();
+ while(len--) {
+ writeByte(value);
+ }
+ waitFully();
+ release();
+ }
+
+ // A full cycle of writing a raw block of data out, including select, release, and waiting
+ template<class D> void writeBytes(uint8_t *data, int len) {
+ uint8_t *end = data + len;
+ select();
+ while(data != end) {
+ writeByte(D::adjust(*data++));
+ }
+ D::postBlock(len);
+ waitFully();
+ release();
+ }
+
+ void writeBytes(uint8_t *data, int len) {
+ writeBytes<DATA_NOP>(data, len);
+ }
+
+ // write a single bit out, which bit from the passed in byte is determined by template parameter
+ template <uint8_t BIT> inline static void writeBit(uint8_t b) {
+ waitFully();
+ NRF_SPI0->ENABLE = 0;
+ if(b & 1<<BIT) {
+ FastPin<_DATA_PIN>::hi();
+ } else {
+ FastPin<_DATA_PIN>::lo();
+ }
+ FastPin<_CLOCK_PIN>::toggle();
+ FastPin<_CLOCK_PIN>::toggle();
+ NRF_SPI0->ENABLE = 1;
+ }
+
+ template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
+ select();
+ int len = pixels.mLen;
+ while(pixels.has(1)) {
+ if(FLAGS & FLAG_START_BIT) {
+ writeBit<0>(1);
+ }
+ writeByte(D::adjust(pixels.loadAndScale0()));
+ writeByte(D::adjust(pixels.loadAndScale1()));
+ writeByte(D::adjust(pixels.loadAndScale2()));
+
+ pixels.advanceData();
+ pixels.stepDithering();
+ }
+ D::postBlock(len);
+ waitFully();
+ release();
+ }
+
+};
+
+#endif
+
+#endif
diff --git a/platforms/arm/nrf51/led_sysdefs_arm_nrf51.h b/platforms/arm/nrf51/led_sysdefs_arm_nrf51.h
new file mode 100644
index 00000000..8b209ff2
--- /dev/null
+++ b/platforms/arm/nrf51/led_sysdefs_arm_nrf51.h
@@ -0,0 +1,44 @@
+#ifndef __LED_SYSDEFS_ARM_NRF51
+#define __LED_SYSDEFS_ARM_NRF51
+
+#ifndef NRF51
+#define NRF51
+#endif
+
+#define LED_TIMER NRF_TIMER1
+#define FASTLED_NO_PINMAP
+#define FASTLED_HAS_CLOCKLESS
+
+#define FASTLED_ARM
+#define FASTLED_ARM_M0
+
+#ifndef F_CPU
+#define F_CPU 16000000
+#endif
+
+#include <stdint.h>
+#include "nrf51.h"
+#include "core_cm0.h"
+
+typedef volatile uint32_t RoReg;
+typedef volatile uint32_t RwReg;
+typedef uint32_t prog_uint32_t;
+typedef uint8_t boolean;
+
+#define PROGMEM
+#define NO_PROGMEM
+#define NEED_CXX_BITS
+
+// Default to NOT using PROGMEM here
+#ifndef FASTLED_USE_PROGMEM
+#define FASTLED_USE_PROGMEM 0
+#endif
+
+#ifndef FASTLED_ALLOW_INTERRUPTS
+#define FASTLED_ALLOW_INTERRUPTS 1
+#endif
+
+#define cli() __disable_irq();
+#define sei() __enable_irq();
+
+#endif
diff --git a/platforms/arm/sam/clockless_arm_sam.h b/platforms/arm/sam/clockless_arm_sam.h
new file mode 100644
index 00000000..4cbdf27f
--- /dev/null
+++ b/platforms/arm/sam/clockless_arm_sam.h
@@ -0,0 +1,145 @@
+#ifndef __INC_CLOCKLESS_ARM_SAM_H
+#define __INC_CLOCKLESS_ARM_SAM_H
+
+FASTLED_NAMESPACE_BEGIN
+
+// Definition for a single channel clockless controller for the sam family of arm chips, like that used in the due and rfduino
+// See clockless.h for detailed info on how the template parameters are used.
+
+#if defined(__SAM3X8E__)
+
+
+#define TADJUST 0
+#define TOTAL ( (T1+TADJUST) + (T2+TADJUST) + (T3+TADJUST) )
+#define T1_MARK (TOTAL - (T1+TADJUST))
+#define T2_MARK (T1_MARK - (T2+TADJUST))
+
+#define SCALE(S,V) scale8_video(S,V)
+// #define SCALE(S,V) scale8(S,V)
+#define FASTLED_HAS_CLOCKLESS 1
+
+template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
+class ClocklessController : public CLEDController {
+ typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
+ typedef typename FastPinBB<DATA_PIN>::port_t data_t;
+
+ data_t mPinMask;
+ data_ptr_t mPort;
+ CMinWait<WAIT_TIME> mWait;
+public:
+ virtual void init() {
+ FastPinBB<DATA_PIN>::setOutput();
+ mPinMask = FastPinBB<DATA_PIN>::mask();
+ mPort = FastPinBB<DATA_PIN>::port();
+ }
+
+ virtual uint16_t getMaxRefreshRate() const { return 400; }
+
+ virtual void clearLeds(int nLeds) {
+ showColor(CRGB(0, 0, 0), nLeds, 0);
+ }
+
+protected:
+
+ // set all the leds on the controller to a given color
+ virtual void showColor(const struct CRGB & rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+ mWait.wait();
+ showRGBInternal(pixels);
+ mWait.mark();
+ }
+
+ virtual void show(const struct CRGB *rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+ mWait.wait();
+ showRGBInternal(pixels);
+ mWait.mark();
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void show(const struct CARGB *rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+ mWait.wait();
+ showRGBInternal(pixels);
+ sei();
+ mWait.mark();
+ }
+#endif
+
+
+ template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register uint8_t & b) {
+ // Make sure we don't slot into a wrapping spot, this will delay up to 12.5µs for WS2812
+ // bool bShift=0;
+ // while(VAL < (TOTAL*10)) { bShift=true; }
+ // if(bShift) { next_mark = (VAL-TOTAL); };
+
+ for(register uint32_t i = BITS; i > 0; i--) {
+ // wait to start the bit, then set the pin high
+ while(DUE_TIMER_VAL < next_mark);
+ next_mark = (DUE_TIMER_VAL+TOTAL);
+ *port = 1;
+
+ // how long we want to wait next depends on whether or not our bit is set to 1 or 0
+ if(b&0x80) {
+ // we're a 1, wait until there's less than T3 clocks left
+ while((next_mark - DUE_TIMER_VAL) > (T3));
+ } else {
+ // we're a 0, wait until there's less than (T2+T3+slop) clocks left in this bit
+ while((next_mark - DUE_TIMER_VAL) > (T2+T3+6+TADJUST+TADJUST));
+ }
+ *port=0;
+ b <<= 1;
+ }
+ }
+
+#define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) )
+ // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+ // gcc will use register Y for the this pointer.
+ static uint32_t showRGBInternal(PixelController<RGB_ORDER> & pixels) {
+ // Setup and start the clock
+ TC_Configure(DUE_TIMER,DUE_TIMER_CHANNEL,TC_CMR_TCCLKS_TIMER_CLOCK1);
+ pmc_enable_periph_clk(DUE_TIMER_ID);
+ TC_Start(DUE_TIMER,DUE_TIMER_CHANNEL);
+
+ register data_ptr_t port asm("r7") = FastPinBB<DATA_PIN>::port(); FORCE_REFERENCE(port);
+ *port = 0;
+
+ // Setup the pixel controller and load/scale the first byte
+ pixels.preStepFirstByteDithering();
+ register uint8_t b = pixels.loadAndScale0();
+
+ uint32_t next_mark = (DUE_TIMER_VAL + (TOTAL));
+ while(pixels.has(1)) {
+ pixels.stepDithering();
+
+ #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ cli();
+ if(DUE_TIMER_VAL > next_mark) {
+ if((DUE_TIMER_VAL - next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL); return DUE_TIMER_VAL; }
+ }
+ #endif
+
+ writeBits<8+XTRA0>(next_mark, port, b);
+
+ b = pixels.loadAndScale1();
+ writeBits<8+XTRA0>(next_mark, port,b);
+
+ b = pixels.loadAndScale2();
+ writeBits<8+XTRA0>(next_mark, port,b);
+
+ b = pixels.advanceAndLoadAndScale0();
+ #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ sei();
+ #endif
+ };
+
+ TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL);
+ return DUE_TIMER_VAL;
+ }
+};
+
+#endif
+
+FASTLED_NAMESPACE_END
+
+#endif
diff --git a/platforms/arm/sam/clockless_block_arm_sam.h b/platforms/arm/sam/clockless_block_arm_sam.h
new file mode 100644
index 00000000..98e80bb3
--- /dev/null
+++ b/platforms/arm/sam/clockless_block_arm_sam.h
@@ -0,0 +1,206 @@
+ #ifndef __INC_BLOCK_CLOCKLESS_H
+#define __INC_BLOCK_CLOCKLESS_H
+
+FASTLED_NAMESPACE_BEGIN
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. The first point
+// is where the line is raised hi. The second pointsnt is where the line is dropped low for a zero. The third point is where the
+// line is dropped low for a one. T1, T2, and T3 correspond to the timings for those three in clock cycles.
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__SAM3X8E__)
+#define PORT_MASK (((1<<LANES)-1) & ((FIRST_PIN==2) ? 0xFF : 0xFF))
+
+#define FASTLED_HAS_BLOCKLESS 1
+
+#define PORTD_FIRST_PIN 25
+#define PORTA_FIRST_PIN 69
+#define PORTB_FIRST_PIN 90
+
+typedef union {
+ uint8_t bytes[8];
+ uint32_t raw[2];
+} Lines;
+
+#define TADJUST 0
+#define TOTAL ( (T1+TADJUST) + (T2+TADJUST) + (T3+TADJUST) )
+#define T1_MARK (TOTAL - (T1+TADJUST))
+#define T2_MARK (T1_MARK - (T2+TADJUST))
+template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
+class InlineBlockClocklessController : public CLEDController {
+ typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t;
+ typedef typename FastPin<FIRST_PIN>::port_t data_t;
+
+ data_t mPinMask;
+ data_ptr_t mPort;
+ CMinWait<WAIT_TIME> mWait;
+public:
+ virtual void init() {
+ if(FIRST_PIN == PORTA_FIRST_PIN) {
+ switch(LANES) {
+ case 8: FastPin<31>::setOutput();
+ case 7: FastPin<58>::setOutput();
+ case 6: FastPin<100>::setOutput();
+ case 5: FastPin<59>::setOutput();
+ case 4: FastPin<60>::setOutput();
+ case 3: FastPin<61>::setOutput();
+ case 2: FastPin<68>::setOutput();
+ case 1: FastPin<69>::setOutput();
+ }
+ } else if(FIRST_PIN == PORTD_FIRST_PIN) {
+ switch(LANES) {
+ case 8: FastPin<11>::setOutput();
+ case 7: FastPin<29>::setOutput();
+ case 6: FastPin<15>::setOutput();
+ case 5: FastPin<14>::setOutput();
+ case 4: FastPin<28>::setOutput();
+ case 3: FastPin<27>::setOutput();
+ case 2: FastPin<26>::setOutput();
+ case 1: FastPin<25>::setOutput();
+ }
+ } else if(FIRST_PIN == PORTB_FIRST_PIN) {
+ switch(LANES) {
+ case 8: FastPin<97>::setOutput();
+ case 7: FastPin<96>::setOutput();
+ case 6: FastPin<95>::setOutput();
+ case 5: FastPin<94>::setOutput();
+ case 4: FastPin<93>::setOutput();
+ case 3: FastPin<92>::setOutput();
+ case 2: FastPin<91>::setOutput();
+ case 1: FastPin<90>::setOutput();
+ }
+ }
+ mPinMask = FastPin<FIRST_PIN>::mask();
+ mPort = FastPin<FIRST_PIN>::port();
+ }
+
+ virtual uint16_t getMaxRefreshRate() const { return 400; }
+
+ virtual void clearLeds(int nLeds) {
+ showColor(CRGB(0, 0, 0), nLeds, 0);
+ }
+
+ // set all the leds on the controller to a given color
+ virtual void showColor(const struct CRGB & rgbdata, int nLeds, CRGB scale) {
+ MultiPixelController<LANES,PORT_MASK,RGB_ORDER> pixels(rgbdata,nLeds, scale, getDither() );
+ mWait.wait();
+ showRGBInternal(pixels, nLeds);
+ sei();
+ mWait.mark();
+ }
+
+// #define ADV_RGB
+#define ADV_RGB if(maskbit & PORT_MASK) { rgbdata += nLeds; } maskbit <<= 1;
+
+ virtual void show(const struct CRGB *rgbdata, int nLeds, CRGB scale) {
+ MultiPixelController<LANES,PORT_MASK,RGB_ORDER> pixels(rgbdata,nLeds, scale, getDither() );
+ mWait.wait();
+ showRGBInternal(pixels, nLeds);
+ mWait.mark();
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void show(const struct CARGB *rgbdata, int nLeds, CRGB scale) {
+ mWait.wait();
+ showRGBInternal(PixelController<RGB_ORDER>(rgbdata, nLeds, scale, getDither()));
+ mWait.mark();
+ }
+#endif
+
+ template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, Lines & b3, MultiPixelController<LANES, PORT_MASK, RGB_ORDER> &pixels) { // , register uint32_t & b2) {
+ register Lines b2;
+ transpose8x1(b.bytes,b2.bytes);
+
+ register uint8_t d = pixels.template getd<PX>(pixels);
+ register uint8_t scale = pixels.template getscale<PX>(pixels);
+
+ for(uint32_t i = 0; (i < LANES) && (i<8); i++) {
+ while(DUE_TIMER_VAL < next_mark);
+ next_mark = (DUE_TIMER_VAL+TOTAL);
+
+ *FastPin<FIRST_PIN>::sport() = PORT_MASK;
+
+ while((next_mark - DUE_TIMER_VAL) > (T2+T3+6));
+ *FastPin<FIRST_PIN>::cport() = (~b2.bytes[7-i]) & PORT_MASK;
+
+ while((next_mark - (DUE_TIMER_VAL)) > T3);
+ *FastPin<FIRST_PIN>::cport() = PORT_MASK;
+
+ b3.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
+ }
+
+ for(uint32_t i = LANES; i < 8; i++) {
+ while(DUE_TIMER_VAL > next_mark);
+
+ next_mark = DUE_TIMER_VAL - (TOTAL-3);
+ *FastPin<FIRST_PIN>::sport() = PORT_MASK;
+
+ while((next_mark - DUE_TIMER_VAL) > (T2+T3+6));
+ *FastPin<FIRST_PIN>::cport() = (~b2.bytes[7-i]) & PORT_MASK;
+
+ while((next_mark - DUE_TIMER_VAL) > T3);
+ *FastPin<FIRST_PIN>::cport() = PORT_MASK;
+ }
+ }
+
+ // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+ // gcc will use register Y for the this pointer.
+ static uint32_t showRGBInternal(MultiPixelController<LANES, PORT_MASK, RGB_ORDER> &allpixels, int nLeds) {
+ // Serial.println("Entering show");
+ // Setup the pixel controller and load/scale the first byte
+ Lines b0,b1,b2;
+
+ allpixels.preStepFirstByteDithering();
+ for(int i = 0; i < LANES; i++) {
+ b0.bytes[i] = allpixels.loadAndScale0(i);
+ }
+
+ // Setup and start the clock
+ TC_Configure(DUE_TIMER,DUE_TIMER_CHANNEL,TC_CMR_TCCLKS_TIMER_CLOCK1);
+ pmc_enable_periph_clk(DUE_TIMER_ID);
+ TC_Start(DUE_TIMER,DUE_TIMER_CHANNEL);
+
+ #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ cli();
+ #endif
+ uint32_t next_mark = (DUE_TIMER_VAL + (TOTAL));
+ while(nLeds--) {
+ allpixels.stepDithering();
+ #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ cli();
+ if(DUE_TIMER_VAL > next_mark) {
+ if((DUE_TIMER_VAL - next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) {
+ sei(); TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL); return DUE_TIMER_VAL;
+ }
+ }
+ #endif
+
+ // Write first byte, read next byte
+ writeBits<8+XTRA0,1>(next_mark, b0, b1, allpixels);
+
+ // Write second byte, read 3rd byte
+ writeBits<8+XTRA0,2>(next_mark, b1, b2, allpixels);
+
+ allpixels.advanceData();
+ // Write third byte
+ writeBits<8+XTRA0,0>(next_mark, b2, b0, allpixels);
+
+ #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ sei();
+ #endif
+ }
+
+ return DUE_TIMER_VAL;
+ }
+
+
+};
+
+#endif
+
+FASTLED_NAMESPACE_END
+
+#endif
diff --git a/platforms/arm/sam/fastled_arm_sam.h b/platforms/arm/sam/fastled_arm_sam.h
new file mode 100644
index 00000000..fd61c14c
--- /dev/null
+++ b/platforms/arm/sam/fastled_arm_sam.h
@@ -0,0 +1,11 @@
+#ifndef __INC_FASTLED_ARM_SAM_H
+#define __INC_FASTLED_ARM_SAM_H
+
+// Include the sam headers
+#include "fastled_delay.h"
+#include "fastpin_arm_sam.h"
+#include "fastspi_arm_sam.h"
+#include "clockless_arm_sam.h"
+#include "clockless_block_arm_sam.h"
+
+#endif
diff --git a/fastpin_arm_sam.h b/platforms/arm/sam/fastpin_arm_sam.h
index 164754c8..2bb78042 100644
--- a/fastpin_arm_sam.h
+++ b/platforms/arm/sam/fastpin_arm_sam.h
@@ -1,11 +1,21 @@
#ifndef __INC_FASTPIN_ARM_SAM_H
#define __INC_FASTPIN_ARM_SAM_H
+FASTLED_NAMESPACE_BEGIN
+
+#if defined(FASTLED_FORCE_SOFTWARE_PINS)
+#warning "Software pin support forced, pin access will be sloightly slower."
+#define NO_HARDWARE_PIN_SUPPORT
+#undef HAS_HARDWARE_PIN_SUPPORT
+
+#else
+
+
/// Template definition for arduino due style ARM pins, providing direct access to the various GPIO registers. Note that this
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
/// The registers are data register, set output register, clear output register, set data direction register
-template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PDDR> class _DUEPIN {
+template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PDDR> class _DUEPIN {
public:
typedef volatile uint32_t * port_ptr_t;
typedef uint32_t port_t;
@@ -18,7 +28,7 @@ public:
inline static void set(register port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
-
+
inline static void toggle() __attribute__ ((always_inline)) { _PDOR::r() ^= _MASK; }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
@@ -28,13 +38,15 @@ public:
inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
+ inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
+ inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
};
-/// Template definition for DUE style ARM pins using bit banding, providing direct access to the various GPIO registers. GCC
+/// Template definition for DUE style ARM pins using bit banding, providing direct access to the various GPIO registers. GCC
/// does a poor job of optimizing around these accesses so they are not being used just yet.
-template<uint8_t PIN, uint32_t _BIT, typename _PDOR, typename _PSOR, typename _PCOR, typename _PDDR> class _DUEPIN_BITBAND {
+template<uint8_t PIN, uint32_t _BIT, typename _PDOR, typename _PSOR, typename _PCOR, typename _PDDR> class _DUEPIN_BITBAND {
public:
typedef volatile uint32_t * port_ptr_t;
typedef uint32_t port_t;
@@ -47,7 +59,7 @@ public:
inline static void set(register port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
-
+
inline static void toggle() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() ^= 1; }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
@@ -60,12 +72,18 @@ public:
inline static port_t mask() __attribute__ ((always_inline)) { return 1; }
};
+#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
+#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))
+
+#define _R(T) struct __gen_struct_ ## T
+#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } \
+ template<int BIT> static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
#define DUE_IO32(L) _RD32(REG_PIO ## L ## _ODSR); _RD32(REG_PIO ## L ## _SODR); _RD32(REG_PIO ## L ## _CODR); _RD32(REG_PIO ## L ## _OER);
#define _DEFPIN_DUE(PIN, BIT, L) template<> class FastPin<PIN> : public _DUEPIN<PIN, 1 << BIT, _R(REG_PIO ## L ## _ODSR), _R(REG_PIO ## L ## _SODR), _R(REG_PIO ## L ## _CODR), \
_R(GPIO ## L ## _OER)> {}; \
template<> class FastPinBB<PIN> : public _DUEPIN_BITBAND<PIN, BIT, _R(REG_PIO ## L ## _ODSR), _R(REG_PIO ## L ## _SODR), _R(REG_PIO ## L ## _CODR), \
- _R(GPIO ## L ## _OER)> {};
+ _R(GPIO ## L ## _OER)> {};
#if defined(__SAM3X8E__)
@@ -96,6 +114,14 @@ _DEFPIN_DUE(68, 1, A); _DEFPIN_DUE(69, 0, A); _DEFPIN_DUE(70, 17, A); _DEFPIN_DU
_DEFPIN_DUE(72, 30, C); _DEFPIN_DUE(73, 21, A); _DEFPIN_DUE(74, 25, A); _DEFPIN_DUE(75, 26, A);
_DEFPIN_DUE(76, 27, A); _DEFPIN_DUE(77, 28, A); _DEFPIN_DUE(78, 23, B);
+// digix pins
+_DEFPIN_DUE(90, 0, B); _DEFPIN_DUE(91, 1, B); _DEFPIN_DUE(92, 2, B); _DEFPIN_DUE(93, 3, B);
+_DEFPIN_DUE(94, 4, B); _DEFPIN_DUE(95, 5, B); _DEFPIN_DUE(96, 6, B); _DEFPIN_DUE(97, 7, B);
+_DEFPIN_DUE(98, 8, B); _DEFPIN_DUE(99, 9, B); _DEFPIN_DUE(100, 5, A); _DEFPIN_DUE(101, 22, B);
+_DEFPIN_DUE(102, 23, B); _DEFPIN_DUE(103, 24, B); _DEFPIN_DUE(104, 27, C); _DEFPIN_DUE(105, 20, C);
+_DEFPIN_DUE(106, 11, C); _DEFPIN_DUE(107, 10, C); _DEFPIN_DUE(108, 21, A); _DEFPIN_DUE(109, 30, C);
+_DEFPIN_DUE(110, 29, B); _DEFPIN_DUE(111, 30, B); _DEFPIN_DUE(112, 31, B); _DEFPIN_DUE(113, 28, B);
+
#define SPI_DATA 75
#define SPI_CLOCK 76
#define ARM_HARDWARE_SPI
@@ -103,5 +129,9 @@ _DEFPIN_DUE(76, 27, A); _DEFPIN_DUE(77, 28, A); _DEFPIN_DUE(78, 23, B);
#endif
+#endif // FASTLED_FORCE_SOFTWARE_PINS
-#endif
+FASTLED_NAMESPACE_END
+
+
+#endif // __INC_FASTPIN_ARM_SAM_H
diff --git a/fastspi_arm_sam.h b/platforms/arm/sam/fastspi_arm_sam.h
index d756c221..eb9abe4c 100644
--- a/fastspi_arm_sam.h
+++ b/platforms/arm/sam/fastspi_arm_sam.h
@@ -1,6 +1,8 @@
#ifndef __INC_FASTSPI_ARM_SAM_H
#define __INC_FASTSPI_ARM_SAM_H
+FASTLED_NAMESPACE_BEGIN
+
#if defined(__SAM3X8E__)
#define m_SPI ((Spi*)SPI0)
@@ -157,4 +159,5 @@ public:
#endif
+FASTLED_NAMESPACE_END
#endif
diff --git a/platforms/arm/sam/led_sysdefs_arm_sam.h b/platforms/arm/sam/led_sysdefs_arm_sam.h
new file mode 100644
index 00000000..a4828648
--- /dev/null
+++ b/platforms/arm/sam/led_sysdefs_arm_sam.h
@@ -0,0 +1,39 @@
+#ifndef __INC_LED_SYSDEFS_ARM_SAM_H
+#define __INC_LED_SYSDEFS_ARM_SAM_H
+
+
+#define FASTLED_ARM
+
+// Setup DUE timer defines/channels/etc...
+#ifndef DUE_TIMER_CHANNEL
+#define DUE_TIMER_GROUP 0
+#endif
+
+#ifndef DUE_TIMER_CHANNEL
+#define DUE_TIMER_CHANNEL 0
+#endif
+
+#define DUE_TIMER ((DUE_TIMER_GROUP==0) ? TC0 : ((DUE_TIMER_GROUP==1) ? TC1 : TC2))
+#define DUE_TIMER_ID (ID_TC0 + (DUE_TIMER_GROUP*3) + DUE_TIMER_CHANNEL)
+#define DUE_TIMER_VAL (DUE_TIMER->TC_CHANNEL[DUE_TIMER_CHANNEL].TC_CV << 1)
+#define DUE_TIMER_RUNNING ((DUE_TIMER->TC_CHANNEL[DUE_TIMER_CHANNEL].TC_SR & TC_SR_CLKSTA) != 0)
+
+#ifndef INTERRUPT_THRESHOLD
+#define INTERRUPT_THRESHOLD 1
+#endif
+
+// Default to allowing interrupts
+#ifndef FASTLED_ALLOW_INTERRUPTS
+#define FASTLED_ALLOW_INTERRUPTS 1
+#endif
+
+#if FASTLED_ALLOW_INTERRUPTS == 1
+#define FASTLED_ACCURATE_CLOCK
+#endif
+
+// reusing/abusing cli/sei defs for due
+#define cli() __disable_irq(); __disable_fault_irq();
+#define sei() __enable_irq(); __enable_fault_irq();
+
+
+#endif
diff --git a/platforms/arm/stm32/clockless_arm_stm32.h b/platforms/arm/stm32/clockless_arm_stm32.h
new file mode 100644
index 00000000..d427abab
--- /dev/null
+++ b/platforms/arm/stm32/clockless_arm_stm32.h
@@ -0,0 +1,147 @@
+#ifndef __INC_CLOCKLESS_ARM_STM32_H
+#define __INC_CLOCKLESS_ARM_STM32_H
+
+FASTLED_NAMESPACE_BEGIN
+// Definition for a single channel clockless controller for the stm32 family of chips, like that used in the spark core
+// See clockless.h for detailed info on how the template parameters are used.
+
+#define FASTLED_HAS_CLOCKLESS 1
+
+template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
+class ClocklessController : public CLEDController {
+ typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
+ typedef typename FastPin<DATA_PIN>::port_t data_t;
+
+ data_t mPinMask;
+ data_ptr_t mPort;
+ CMinWait<WAIT_TIME> mWait;
+public:
+ virtual void init() {
+ FastPin<DATA_PIN>::setOutput();
+ mPinMask = FastPin<DATA_PIN>::mask();
+ mPort = FastPin<DATA_PIN>::port();
+ }
+
+ virtual uint16_t getMaxRefreshRate() const { return 400; }
+
+ virtual void clearLeds(int nLeds) {
+ showColor(CRGB(0, 0, 0), nLeds, 0);
+ }
+
+protected:
+
+ // set all the leds on the controller to a given color
+ virtual void showColor(const struct CRGB & rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+
+ mWait.wait();
+ showRGBInternal(pixels);
+ mWait.mark();
+ }
+
+ virtual void show(const struct CRGB *rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+
+ mWait.wait();
+ showRGBInternal(pixels);
+ mWait.mark();
+ }
+
+ #ifdef SUPPORT_ARGB
+ virtual void show(const struct CARGB *rgbdata, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(rgbdata, nLeds, scale, getDither());
+ mWait.wait();
+ showRGBInternal(pixels);
+ mWait.mark();
+ }
+ #endif
+
+#define _CYCCNT (*(volatile uint32_t*)(0xE0001004UL))
+
+ template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t & b) {
+ for(register uint32_t i = BITS-1; i > 0; i--) {
+ while(_CYCCNT < (T1+T2+T3-20));
+ FastPin<DATA_PIN>::fastset(port, hi);
+ _CYCCNT = 4;
+ if(b&0x80) {
+ while(_CYCCNT < (T1+T2-20));
+ FastPin<DATA_PIN>::fastset(port, lo);
+ } else {
+ while(_CYCCNT < (T1-10));
+ FastPin<DATA_PIN>::fastset(port, lo);
+ }
+ b <<= 1;
+ }
+
+ while(_CYCCNT < (T1+T2+T3-20));
+ FastPin<DATA_PIN>::fastset(port, hi);
+ _CYCCNT = 4;
+
+ if(b&0x80) {
+ while(_CYCCNT < (T1+T2-20));
+ FastPin<DATA_PIN>::fastset(port, lo);
+ } else {
+ while(_CYCCNT < (T1-10));
+ FastPin<DATA_PIN>::fastset(port, lo);
+ }
+ }
+
+ // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+ // gcc will use register Y for the this pointer.
+ static uint32_t showRGBInternal(PixelController<RGB_ORDER> & pixels) {
+ // Get access to the clock
+ CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
+ DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
+ DWT->CYCCNT = 0;
+
+ register data_ptr_t port = FastPin<DATA_PIN>::port();
+ register data_t hi = *port | FastPin<DATA_PIN>::mask();;
+ register data_t lo = *port & ~FastPin<DATA_PIN>::mask();;
+ *port = lo;
+
+ // Setup the pixel controller and load/scale the first byte
+ pixels.preStepFirstByteDithering();
+ register uint8_t b = pixels.loadAndScale0();
+
+ cli();
+
+ uint32_t next_mark = (T1+T2+T3);
+
+ DWT->CYCCNT = 0;
+ while(pixels.has(1)) {
+ pixels.stepDithering();
+ #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ cli();
+ // if interrupts took longer than 45µs, punt on the current frame
+ if(DWT->CYCCNT > next_mark) {
+ if((DWT->CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return DWT->CYCCNT; }
+ }
+
+ hi = *port | FastPin<DATA_PIN>::mask();
+ lo = *port & ~FastPin<DATA_PIN>::mask();
+ #endif
+
+ // Write first byte, read next byte
+ writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
+ b = pixels.loadAndScale1();
+
+ // Write second byte, read 3rd byte
+ writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
+ b = pixels.loadAndScale2();
+
+ // Write third byte, read 1st byte of next pixel
+ writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
+ b = pixels.advanceAndLoadAndScale0();
+ #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ sei();
+ #endif
+ };
+
+ sei();
+ return DWT->CYCCNT;
+ }
+};
+
+FASTLED_NAMESPACE_END
+
+ #endif
diff --git a/platforms/arm/stm32/fastled_arm_stm32.h b/platforms/arm/stm32/fastled_arm_stm32.h
new file mode 100644
index 00000000..e790bfec
--- /dev/null
+++ b/platforms/arm/stm32/fastled_arm_stm32.h
@@ -0,0 +1,10 @@
+#ifndef __INC_FASTLED_ARM_SAM_H
+#define __INC_FASTLED_ARM_SAM_H
+
+// Include the sam headers
+#include "fastled_delay.h"
+#include "fastpin_arm_stm32.h"
+// #include "fastspi_arm_stm32.h"
+#include "clockless_arm_stm32.h"
+
+#endif
diff --git a/platforms/arm/stm32/fastpin_arm_stm32.h b/platforms/arm/stm32/fastpin_arm_stm32.h
new file mode 100644
index 00000000..63729bba
--- /dev/null
+++ b/platforms/arm/stm32/fastpin_arm_stm32.h
@@ -0,0 +1,105 @@
+#ifndef __FASTPIN_ARM_STM32_H
+#define __FASTPIN_ARM_STM32_H
+
+FASTLED_NAMESPACE_BEGIN
+
+#if defined(FASTLED_FORCE_SOFTWARE_PINS)
+#warning "Software pin support forced, pin access will be sloightly slower."
+#define NO_HARDWARE_PIN_SUPPORT
+#undef HAS_HARDWARE_PIN_SUPPORT
+
+#else
+
+/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this
+/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
+/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
+/// The registers are data output, set output, clear output, toggle output, input, and direction
+
+template<uint8_t PIN, uint8_t _BIT, uint32_t _MASK, typename _GPIO> class _ARMPIN {
+public:
+ typedef volatile uint32_t * port_ptr_t;
+ typedef uint32_t port_t;
+
+ #if 0
+ inline static void setOutput() {
+ if(_BIT<8) {
+ _CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4));
+ } else {
+ _CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4));
+ }
+ }
+ inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
+ #endif
+
+ inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
+ inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
+
+ inline static void hi() __attribute__ ((always_inline)) { _GPIO::r()->BSRR = _MASK; }
+ inline static void lo() __attribute__ ((always_inline)) { _GPIO::r()->BRR = _MASK; }
+ // inline static void lo() __attribute__ ((always_inline)) { _GPIO::r()->BSRR = (_MASK<<16); }
+ inline static void set(register port_t val) __attribute__ ((always_inline)) { _GPIO::r()->ODR = val; }
+
+ inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
+
+ inline static void toggle() __attribute__ ((always_inline)) { if(_GPIO::r()->ODR & _MASK) { lo(); } else { hi(); } }
+
+ inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
+ inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
+ inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
+
+ inline static port_t hival() __attribute__ ((always_inline)) { return _GPIO::r()->ODR | _MASK; }
+ inline static port_t loval() __attribute__ ((always_inline)) { return _GPIO::r()->ODR & ~_MASK; }
+ inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_GPIO::r()->ODR; }
+ inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPIO::r()->BSRR; }
+ inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPIO::r()->BRR; }
+ inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
+};
+
+#define _R(T) struct __gen_struct_ ## T
+#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline volatile GPIO_TypeDef * r() { return T; } };
+
+#define _IO32(L) _RD32(GPIO ## L)
+
+#define _DEFPIN_ARM(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1 << BIT, _R(GPIO ## L)> {};
+
+// Actual pin definitions
+#if defined(SPARK)
+
+_IO32(A); _IO32(B); _IO32(C); _IO32(D); _IO32(E); _IO32(F); _IO32(G);
+
+
+#define MAX_PIN 19
+_DEFPIN_ARM(0, 7, B);
+_DEFPIN_ARM(1, 6, B);
+_DEFPIN_ARM(2, 5, B);
+_DEFPIN_ARM(3, 4, B);
+_DEFPIN_ARM(4, 3, B);
+_DEFPIN_ARM(5, 15, A);
+_DEFPIN_ARM(6, 14, A);
+_DEFPIN_ARM(7, 13, A);
+_DEFPIN_ARM(8, 8, A);
+_DEFPIN_ARM(9, 9, A);
+_DEFPIN_ARM(10, 0, A);
+_DEFPIN_ARM(11, 1, A);
+_DEFPIN_ARM(12, 4, A);
+_DEFPIN_ARM(13, 5, A);
+_DEFPIN_ARM(14, 6, A);
+_DEFPIN_ARM(15, 7, A);
+_DEFPIN_ARM(16, 0, B);
+_DEFPIN_ARM(17, 1, B);
+_DEFPIN_ARM(18, 3, A);
+_DEFPIN_ARM(19, 2, A);
+
+
+#define SPI_DATA 15
+#define SPI_CLOCK 13
+
+#define HAS_HARDWARE_PIN_SUPPORT
+
+#endif
+
+#endif // FASTLED_FORCE_SOFTWARE_PINS
+
+FASTLED_NAMESPACE_END
+
+#endif // __INC_FASTPIN_ARM_STM32
diff --git a/platforms/arm/stm32/led_sysdefs_arm_stm32.h b/platforms/arm/stm32/led_sysdefs_arm_stm32.h
new file mode 100644
index 00000000..c15e001c
--- /dev/null
+++ b/platforms/arm/stm32/led_sysdefs_arm_stm32.h
@@ -0,0 +1,47 @@
+#ifndef __INC_LED_SYSDEFS_ARM_SAM_H
+#define __INC_LED_SYSDEFS_ARM_SAM_H
+
+#include "application.h"
+
+#define FASTLED_NAMESPACE_BEGIN namespace NSFastLED {
+#define FASTLED_NAMESPACE_END }
+#define FASTLED_USING_NAMESPACE using namespace NSFastLED;
+
+#define FASTLED_ARM
+
+#ifndef INTERRUPT_THRESHOLD
+#define INTERRUPT_THRESHOLD 1
+#endif
+
+// Default to allowing interrupts
+#ifndef FASTLED_ALLOW_INTERRUPTS
+#define FASTLED_ALLOW_INTERRUPTS 0
+#endif
+
+#if FASTLED_ALLOW_INTERRUPTS == 1
+#define FASTLED_ACCURATE_CLOCK
+#endif
+
+// reusing/abusing cli/sei defs for due
+#define cli() __disable_irq(); __disable_fault_irq();
+#define sei() __enable_irq(); __enable_fault_irq();
+
+// pgmspace definitions
+#define PROGMEM
+#define pgm_read_dword(addr) (*(const unsigned long *)(addr))
+#define pgm_read_dword_near(addr) pgm_read_dword(addr)
+
+// Default to NOT using PROGMEM here
+#ifndef FASTLED_USE_PROGMEM
+#define FASTLED_USE_PROGMEM 0
+#endif
+
+// data type defs
+typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
+typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
+
+#define FASTLED_NO_PINMAP
+
+#define F_CPU 72000000
+
+#endif
diff --git a/platforms/avr/clockless_trinket.h b/platforms/avr/clockless_trinket.h
new file mode 100644
index 00000000..7ddb0649
--- /dev/null
+++ b/platforms/avr/clockless_trinket.h
@@ -0,0 +1,514 @@
+#ifndef __INC_CLOCKLESS_TRINKET_H
+#define __INC_CLOCKLESS_TRINKET_H
+
+#include "controller.h"
+#include "lib8tion.h"
+#include <avr/interrupt.h> // for cli/se definitions
+
+FASTLED_NAMESPACE_BEGIN
+
+#if defined(FASTLED_AVR)
+
+// Scaling macro choice
+#ifndef TRINKET_SCALE
+#define TRINKET_SCALE 1
+// whether or not to use dithering
+#define DITHER 1
+#endif
+
+#if (F_CPU==8000000)
+#define FASTLED_SLOW_CLOCK_ADJUST asm __volatile__ ("mov r0,r0\n\t");
+#else
+#define FASTLED_SLOW_CLOCK_ADJUST
+#endif
+
+#define US_PER_TICK (64 / (F_CPU/1000000))
+
+// Variations on the functions in delay.h - w/a loop var passed in to preserve registers across calls by the optimizer/compiler
+template<int CYCLES> inline void _dc(register uint8_t & loopvar);
+
+template<int _LOOP, int PAD> __attribute__((always_inline)) inline void _dc_AVR(register uint8_t & loopvar) {
+ _dc<PAD>(loopvar);
+ // The convolution in here is to ensure that the state of the carry flag coming into the delay loop is preserved
+ asm __volatile__ ( "BRCS L_PC%=\n\t"
+ " LDI %[loopvar], %[_LOOP]\n\tL_%=: DEC %[loopvar]\n\t BRNE L_%=\n\tBREQ L_DONE%=\n\t"
+ "L_PC%=: LDI %[loopvar], %[_LOOP]\n\tLL_%=: DEC %[loopvar]\n\t BRNE LL_%=\n\tBSET 0\n\t"
+ "L_DONE%=:\n\t"
+ :
+ [loopvar] "+a" (loopvar) : [_LOOP] "M" (_LOOP) : );
+}
+
+template<int CYCLES> __attribute__((always_inline)) inline void _dc(register uint8_t & loopvar) {
+ _dc_AVR<CYCLES/6,CYCLES%6>(loopvar);
+}
+template<> __attribute__((always_inline)) inline void _dc<-6>(register uint8_t & loopvar) {}
+template<> __attribute__((always_inline)) inline void _dc<-5>(register uint8_t & loopvar) {}
+template<> __attribute__((always_inline)) inline void _dc<-4>(register uint8_t & loopvar) {}
+template<> __attribute__((always_inline)) inline void _dc<-3>(register uint8_t & loopvar) {}
+template<> __attribute__((always_inline)) inline void _dc<-2>(register uint8_t & loopvar) {}
+template<> __attribute__((always_inline)) inline void _dc<-1>(register uint8_t & loopvar) {}
+template<> __attribute__((always_inline)) inline void _dc< 0>(register uint8_t & loopvar) {}
+template<> __attribute__((always_inline)) inline void _dc< 1>(register uint8_t & loopvar) {asm __volatile__("mov r0,r0":::);}
+template<> __attribute__((always_inline)) inline void _dc< 2>(register uint8_t & loopvar) {asm __volatile__("rjmp .+0":::);}
+template<> __attribute__((always_inline)) inline void _dc< 3>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<1>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc< 4>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<2>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc< 5>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<3>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc< 6>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<2>(loopvar); _dc<2>(loopvar);}
+template<> __attribute__((always_inline)) inline void _dc< 7>(register uint8_t & loopvar) { _dc<4>(loopvar); _dc<3>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc< 8>(register uint8_t & loopvar) { _dc<4>(loopvar); _dc<4>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc< 9>(register uint8_t & loopvar) { _dc<5>(loopvar); _dc<4>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc<10>(register uint8_t & loopvar) { _dc<6>(loopvar); _dc<4>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc<11>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<1>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc<12>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<2>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc<13>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<3>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc<14>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<4>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc<15>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<5>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc<16>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<6>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc<17>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<7>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc<18>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<8>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc<19>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<9>(loopvar); }
+template<> __attribute__((always_inline)) inline void _dc<20>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<10>(loopvar); }
+
+#define DINTPIN(T,ADJ,PINADJ) (T-(PINADJ+ADJ)>0) ? _dc<T-(PINADJ+ADJ)>(loopvar) : _dc<0>(loopvar);
+#define DINT(T,ADJ) if(AVR_PIN_CYCLES(DATA_PIN)==1) { DINTPIN(T,ADJ,1) } else { DINTPIN(T,ADJ,2); }
+#define D1(ADJ) DINT(T1,ADJ)
+#define D2(ADJ) DINT(T2,ADJ)
+#define D3(ADJ) DINT(T3,ADJ)
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. The first point
+// is where the line is raised hi. The second point is where the line is dropped low for a zero. The third point is where the
+// line is dropped low for a one. T1, T2, and T3 correspond to the timings for those three in clock cycles.
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if (!defined(NO_CORRECTION) || (NO_CORRECTION == 0)) && (FASTLED_ALLOW_INTERRUPTS == 0)
+static uint8_t gTimeErrorAccum256ths;
+#endif
+
+#define FASTLED_HAS_CLOCKLESS 1
+
+template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 10>
+class ClocklessController : public CLEDController {
+ typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
+ typedef typename FastPin<DATA_PIN>::port_t data_t;
+
+ CMinWait<WAIT_TIME> mWait;
+public:
+ virtual void init() {
+ FastPin<DATA_PIN>::setOutput();
+ }
+
+ virtual uint16_t getMaxRefreshRate() const { return 400; }
+
+ virtual void clearLeds(int nLeds) {
+ CRGB zeros(0,0,0);
+ showAdjTime((uint8_t*)&zeros, nLeds, zeros, false, 0);
+ }
+
+protected:
+
+ // set all the leds on the controller to a given color
+ virtual void showColor(const struct CRGB & rgbdata, int nLeds, CRGB scale) {
+ showAdjTime((uint8_t*)&rgbdata, nLeds, scale, false, 0);
+ }
+
+ virtual void show(const struct CRGB *rgbdata, int nLeds, CRGB scale) {
+ showAdjTime((uint8_t*)rgbdata, nLeds, scale, true, 0);
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void show(const struct CARGB *rgbdata, int nLeds, CRGB scale) {
+ showAdjTime((uint8_t*)rgbdata, nLeds, scale, true, 1);
+ }
+#endif
+
+ void showAdjTime(const uint8_t *data, int nLeds, CRGB & scale, bool advance, int skip) {
+ PixelController<RGB_ORDER> pixels(data, nLeds, scale, getDither(), advance, skip);
+
+ mWait.wait();
+ cli();
+
+ showRGBInternal(pixels);
+
+ // Adjust the timer
+#if (!defined(NO_CORRECTION) || (NO_CORRECTION == 0)) && (FASTLED_ALLOW_INTERRUPTS == 0)
+ uint32_t microsTaken = (uint32_t)nLeds * (uint32_t)CLKS_TO_MICROS(24 * (T1 + T2 + T3));
+
+ // adust for approximate observed actal runtime (as of January 2015)
+ // roughly 9.6 cycles per pixel, which is 0.6us/pixel at 16MHz
+ // microsTaken += nLeds * 0.6 * CLKS_TO_MICROS(16);
+ microsTaken += scale16by8(nLeds,(0.6 * 256) + 1) * CLKS_TO_MICROS(16);
+
+ // if less than 1000us, there is NO timer impact,
+ // this is because the ONE interrupt that might come in while interrupts
+ // are disabled is queued up, and it will be serviced as soon as
+ // interrupts are re-enabled.
+ // This actually should technically also account for the runtime of the
+ // interrupt handler itself, but we're just not going to worry about that.
+ if( microsTaken > 1000) {
+
+ // Since up to one timer tick will be queued, we don't need
+ // to adjust the MS_COUNTER for that one.
+ microsTaken -= 1000;
+
+ // Now convert microseconds to 256ths of a second, approximately like this:
+ // 250ths = (us/4)
+ // 256ths = 250ths * (263/256);
+ uint16_t x256ths = microsTaken >> 2;
+ x256ths += scale16by8(x256ths,7);
+
+ x256ths += gTimeErrorAccum256ths;
+ MS_COUNTER += (x256ths >> 8);
+ gTimeErrorAccum256ths = x256ths & 0xFF;
+ }
+
+#if 0
+ // For pixel counts of 30 and under at 16Mhz, no correction is necessary.
+ // For pixel counts of 15 and under at 8Mhz, no correction is necessary.
+ //
+ // This code, below, is smaller, and quicker clock correction, which drifts much
+ // more significantly, but is a few bytes smaller. Presented here for consideration
+ // as an alternate on the ATtiny, which can't have more than about 150 pixels MAX
+ // anyway, meaning that microsTaken will never be more than about 4,500, which fits in
+ // a 16-bit variable. The difference between /1000 and /1024 only starts showing
+ // up in the range of about 100 pixels, so many ATtiny projects won't even
+ // see a clock difference due to the approximation there.
+ uint16_t microsTaken = (uint32_t)nLeds * (uint32_t)CLKS_TO_MICROS((24) * (T1 + T2 + T3));
+ MS_COUNTER += (microsTaken >> 10);
+#endif
+
+#endif
+
+ sei();
+ mWait.mark();
+ }
+#define USE_ASM_MACROS
+
+// The variables that our various asm statemetns use. The same block of variables needs to be declared for
+// all the asm blocks because GCC is pretty stupid and it would clobber variables happily or optimize code away too aggressively
+#define ASM_VARS : /* write variables */ \
+ [count] "+x" (count), \
+ [data] "+z" (data), \
+ [b1] "+a" (b1), \
+ [d0] "+r" (d0), \
+ [d1] "+r" (d1), \
+ [d2] "+r" (d2), \
+ [loopvar] "+a" (loopvar), \
+ [scale_base] "+a" (scale_base) \
+ : /* use variables */ \
+ [ADV] "r" (advanceBy), \
+ [b0] "a" (b0), \
+ [hi] "r" (hi), \
+ [lo] "r" (lo), \
+ [s0] "r" (s0), \
+ [s1] "r" (s1), \
+ [s2] "r" (s2), \
+ [e0] "r" (e0), \
+ [e1] "r" (e1), \
+ [e2] "r" (e2), \
+ [PORT] "M" (FastPin<DATA_PIN>::port()-0x20), \
+ [O0] "M" (RGB_BYTE0(RGB_ORDER)), \
+ [O1] "M" (RGB_BYTE1(RGB_ORDER)), \
+ [O2] "M" (RGB_BYTE2(RGB_ORDER)) \
+ : "cc" /* clobber registers */
+
+
+// Note: the code in the else in HI1/LO1 will be turned into an sts (2 cycle, 2 word) opcode
+// 1 cycle, write hi to the port
+#define HI1 FASTLED_SLOW_CLOCK_ADJUST if((int)(FastPin<DATA_PIN>::port())-0x20 < 64) { asm __volatile__("out %[PORT], %[hi]" ASM_VARS ); } else { *FastPin<DATA_PIN>::port()=hi; }
+// 1 cycle, write lo to the port
+#define LO1 if((int)(FastPin<DATA_PIN>::port())-0x20 < 64) { asm __volatile__("out %[PORT], %[lo]" ASM_VARS ); } else { *FastPin<DATA_PIN>::port()=lo; }
+
+// 2 cycles, sbrs on flipping the line to lo if we're pushing out a 0
+#define QLO2(B, N) asm __volatile__("sbrs %[" #B "], " #N ASM_VARS ); LO1;
+// load a byte from ram into the given var with the given offset
+#define LD2(B,O) asm __volatile__("ldd %[" #B "], Z + %[" #O "]\n\t" ASM_VARS );
+// 4 cycles - load a byte from ram into the scaling scratch space with the given offset, clear the target var, clear carry
+#define LDSCL4(B,O) asm __volatile__("ldd %[scale_base], Z + %[" #O "]\n\tclr %[" #B "]\n\tclc\n\t" ASM_VARS );
+
+#if (DITHER==1)
+// apply dithering value before we do anything with scale_base
+#define PRESCALE4(D) asm __volatile__("cpse %[scale_base], __zero_reg__\n\t add %[scale_base],%[" #D "]\n\tbrcc L_%=\n\tldi %[scale_base], 0xFF\n\tL_%=:\n\t" ASM_VARS);
+
+// Do the add for the prescale
+#define PRESCALEA2(D) asm __volatile__("cpse %[scale_base], __zero_reg__\n\t add %[scale_base],%[" #D "]\n\t" ASM_VARS);
+
+// Do the clamp for the prescale, clear carry when we're done - NOTE: Must ensure carry flag state is preserved!
+#define PRESCALEB3(D) asm __volatile__("brcc L_%=\n\tldi %[scale_base], 0xFF\n\tL_%=:\n\tCLC" ASM_VARS);
+
+#else
+#define PRESCALE4(D) _dc<4>(loopvar);
+#define PRESCALEA2(D) _dc<2>(loopvar);
+#define PRESCALEB3(D) _dc<3>(loopvar);
+#endif
+
+// 2 cycles - perform one step of the scaling (if a given bit is set in scale, add scale-base to the scratch space)
+#define _SCALE02(B, N) "sbrc %[s0], " #N "\n\tadd %[" #B "], %[scale_base]\n\t"
+#define _SCALE12(B, N) "sbrc %[s1], " #N "\n\tadd %[" #B "], %[scale_base]\n\t"
+#define _SCALE22(B, N) "sbrc %[s2], " #N "\n\tadd %[" #B "], %[scale_base]\n\t"
+#define SCALE02(B,N) asm __volatile__( _SCALE02(B,N) ASM_VARS );
+#define SCALE12(B,N) asm __volatile__( _SCALE12(B,N) ASM_VARS );
+#define SCALE22(B,N) asm __volatile__( _SCALE22(B,N) ASM_VARS );
+
+// 1 cycle - rotate right, pulling in from carry
+#define _ROR1(B) "ror %[" #B "]\n\t"
+#define ROR1(B) asm __volatile__( _ROR1(B) ASM_VARS);
+
+// 1 cycle, clear the carry bit
+#define _CLC1 "clc\n\t"
+#define CLC1 asm __volatile__( _CLC1 ASM_VARS );
+
+// 2 cycles, rortate right, pulling in from carry then clear the carry bit
+#define RORCLC2(B) asm __volatile__( _ROR1(B) _CLC1 ASM_VARS );
+
+// 4 cycles, rotate, clear carry, scale next bit
+#define RORSC04(B, N) asm __volatile__( _ROR1(B) _CLC1 _SCALE02(B, N) ASM_VARS );
+#define RORSC14(B, N) asm __volatile__( _ROR1(B) _CLC1 _SCALE12(B, N) ASM_VARS );
+#define RORSC24(B, N) asm __volatile__( _ROR1(B) _CLC1 _SCALE22(B, N) ASM_VARS );
+
+// 4 cycles, scale bit, rotate, clear carry
+#define SCROR04(B, N) asm __volatile__( _SCALE02(B,N) _ROR1(B) _CLC1 ASM_VARS );
+#define SCROR14(B, N) asm __volatile__( _SCALE12(B,N) _ROR1(B) _CLC1 ASM_VARS );
+#define SCROR24(B, N) asm __volatile__( _SCALE22(B,N) _ROR1(B) _CLC1 ASM_VARS );
+
+/////////////////////////////////////////////////////////////////////////////////////
+// Loop life cycle
+
+// dither adjustment macro - should be kept in sync w/what's in stepDithering
+// #define ADJDITHER2(D, E) D = E - D;
+#define ADJDITHER2(D, E) asm __volatile__ ("neg %[" #D "]\n\tadd %[" #D "],%[" #E "]\n\t" ASM_VARS);
+
+// #define xstr(a) str(a)
+// #define str(a) #a
+// #define ADJDITHER2(D,E) asm __volatile__("subi %[" #D "], " xstr(DUSE) "\n\tand %[" #D "], %[" #E "]\n\t" ASM_VARS);
+
+// define the beginning of the loop
+#define LOOP asm __volatile__("1:" ASM_VARS );
+// define the end of the loop
+#define DONE asm __volatile__("2:" ASM_VARS );
+
+// 2 cycles - increment the data pointer
+#define IDATA2 asm __volatile__("add %A[data], %[ADV]\n\tadc %B[data], __zero_reg__\n\t" ASM_VARS );
+#define IDATACLC3 asm __volatile__("add %A[data], %[ADV]\n\tadc %B[data], __zero_reg__\n\t" _CLC1 ASM_VARS );
+
+// 1 cycle mov
+#define MOV1(B1, B2) asm __volatile__("mov %[" #B1 "], %[" #B2 "]" ASM_VARS );
+
+// 2 cycles - decrement the counter
+#define DCOUNT2 asm __volatile__("sbiw %[count], 1" ASM_VARS );
+// 2 cycles - jump to the beginning of the loop
+#define JMPLOOP2 asm __volatile__("rjmp 1b" ASM_VARS );
+// 2 cycles - jump out of the loop
+#define BRLOOP1 asm __volatile__("brne 3\n\trjmp 2f\n\t3:" ASM_VARS );
+
+// 5 cycles 2 sbiw, 3 for the breq/rjmp
+#define ENDLOOP5 asm __volatile__("sbiw %[count], 1\n\tbreq L_%=\n\trjmp 1b\n\tL_%=:\n\t" ASM_VARS);
+
+// NOP using the variables, forcing a move
+#define DNOP asm __volatile__("mov r0,r0" ASM_VARS);
+
+#define DADVANCE 3
+#define DUSE (0xFF - (DADVANCE-1))
+
+ // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+ // gcc will use register Y for the this pointer.
+ static void /*__attribute__((optimize("O0")))*/ /*__attribute__ ((always_inline))*/ showRGBInternal(PixelController<RGB_ORDER> & pixels) {
+ uint8_t *data = (uint8_t*)pixels.mData;
+ data_ptr_t port = FastPin<DATA_PIN>::port();
+ data_t mask = FastPin<DATA_PIN>::mask();
+ uint8_t scale_base = 0;
+
+ // register uint8_t *end = data + nLeds;
+ data_t hi = *port | mask;
+ data_t lo = *port & ~mask;
+ *port = lo;
+
+ // the byte currently being written out
+ uint8_t b0 = 0;
+ // the byte currently being worked on to write the next out
+ uint8_t b1 = 0;
+
+ // Setup the pixel controller
+ pixels.preStepFirstByteDithering();
+
+ // pull the dithering/adjustment values out of the pixels object for direct asm access
+ uint8_t advanceBy = pixels.advanceBy();
+ uint16_t count = pixels.mLen;
+
+ uint8_t s0 = pixels.mScale.raw[RO(0)];
+ uint8_t s1 = pixels.mScale.raw[RO(1)];
+ uint8_t s2 = pixels.mScale.raw[RO(2)];
+ uint8_t d0 = pixels.d[RO(0)];
+ uint8_t d1 = pixels.d[RO(1)];
+ uint8_t d2 = pixels.d[RO(2)];
+ uint8_t e0 = pixels.e[RO(0)];
+ uint8_t e1 = pixels.e[RO(1)];
+ uint8_t e2 = pixels.e[RO(2)];
+
+ uint8_t loopvar=0;
+
+ // load/scale the first byte
+#if !defined(LIB8_ATTINY)
+ // we have a hardware multiply, can use loadAndScale0
+ b0 = pixels.loadAndScale0();
+#else
+ // no hardware multiply, we have to do our own mul by hand here, lest we incur a
+ // function call which will kill all of our register usage/allocations below
+ b0 = data[RO(0)];
+ {
+ LDSCL4(b0,O0) PRESCALEA2(d0)
+ PRESCALEB3(d0) SCALE02(b0,0)
+ RORSC04(b0,1) ROR1(b0) CLC1
+ SCROR04(b0,2) SCALE02(b0,3)
+ RORSC04(b0,4) ROR1(b0) CLC1
+ SCROR04(b0,5) SCALE02(b0,6)
+ RORSC04(b0,7) ROR1(b0) CLC1
+ }
+#endif
+
+ // #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ // TCCR0A |= 0x30;
+ // OCR0B = (uint8_t)(TCNT0 + ((WAIT_TIME-INTERRUPT_THRESHOLD)/US_PER_TICK));
+ // TIFR0 = 0x04;
+ // #endif
+ {
+ // while(--count)
+ {
+ // Loop beginning, does some stuff that's outside of the pixel write cycle, namely incrementing d0-2 and masking off
+ // by the E values (see the definition )
+ DNOP;
+ LOOP;
+
+ // ADJDITHER2(d0,e0);
+ // ADJDITHER2(d1,e1);
+ // ADJDITHER2(d2,e2);
+ // NOP;
+ // #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ // cli();
+ // if(TIFR0 & 0x04) {
+ // sei();
+ // TCCR0A &= ~0x30;
+ // return;
+ // }
+ // hi = *port | mask;
+ // lo = *port & ~mask;
+ // #endif
+
+ // Sum of the clock counts across each row should be 10 for 8Mhz, WS2811
+ // The values in the D1/D2/D3 indicate how many cycles the previous column takes
+ // to allow things to line back up.
+ //
+ // While writing out byte 0, we're loading up byte 1, applying the dithering adjustment,
+ // then scaling it using 8 cycles of shift/add interleaved in between writing the bits
+ // out. When doing byte 1, we're doing the above for byte 2. When we're doing byte 2,
+ // we're cycling back around and doing the above for byte 0.
+#if TRINKET_SCALE
+ // Inline scaling - RGB ordering
+ // DNOP
+ HI1 D1(1) QLO2(b0, 7) LDSCL4(b1,O1) D2(4) LO1 PRESCALEA2(d1) D3(2)
+ HI1 D1(1) QLO2(b0, 6) PRESCALEB3(d1) D2(3) LO1 SCALE12(b1,0) D3(2)
+ HI1 D1(1) QLO2(b0, 5) RORSC14(b1,1) D2(4) LO1 RORCLC2(b1) D3(2)
+ HI1 D1(1) QLO2(b0, 4) SCROR14(b1,2) D2(4) LO1 SCALE12(b1,3) D3(2)
+ HI1 D1(1) QLO2(b0, 3) RORSC14(b1,4) D2(4) LO1 RORCLC2(b1) D3(2)
+ HI1 D1(1) QLO2(b0, 2) SCROR14(b1,5) D2(4) LO1 SCALE12(b1,6) D3(2)
+ HI1 D1(1) QLO2(b0, 1) RORSC14(b1,7) D2(4) LO1 RORCLC2(b1) D3(2)
+ HI1 D1(1) QLO2(b0, 0)
+ switch(XTRA0) {
+ case 4: D2(0) LO1 D3(0) HI1 D1(1) QLO2(b0,0)
+ case 3: D2(0) LO1 D3(0) HI1 D1(1) QLO2(b0,0)
+ case 2: D2(0) LO1 D3(0) HI1 D1(1) QLO2(b0,0)
+ case 1: D2(0) LO1 D3(0) HI1 D1(1) QLO2(b0,0)
+ }
+ ADJDITHER2(d1,e1) D2(2) LO1 MOV1(b0,b1) D3(1)
+
+ HI1 D1(1) QLO2(b0, 7) LDSCL4(b1,O2) D2(4) LO1 PRESCALEA2(d2) D3(2)
+ HI1 D1(1) QLO2(b0, 6) PRESCALEB3(d2) D2(3) LO1 SCALE22(b1,0) D3(2)
+ HI1 D1(1) QLO2(b0, 5) RORSC24(b1,1) D2(4) LO1 RORCLC2(b1) D3(2)
+ HI1 D1(1) QLO2(b0, 4) SCROR24(b1,2) D2(4) LO1 SCALE22(b1,3) D3(2)
+ HI1 D1(1) QLO2(b0, 3) RORSC24(b1,4) D2(4) LO1 RORCLC2(b1) D3(2)
+ HI1 D1(1) QLO2(b0, 2) SCROR24(b1,5) D2(4) LO1 SCALE22(b1,6) D3(2)
+ HI1 D1(1) QLO2(b0, 1) RORSC24(b1,7) D2(4) LO1 RORCLC2(b1) D3(2)
+ HI1 D1(1) QLO2(b0, 0)
+ switch(XTRA0) {
+ case 4: D2(0) LO1 D3(0) HI1 D1(1) QLO2(b0,0)
+ case 3: D2(0) LO1 D3(0) HI1 D1(1) QLO2(b0,0)
+ case 2: D2(0) LO1 D3(0) HI1 D1(1) QLO2(b0,0)
+ case 1: D2(0) LO1 D3(0) HI1 D1(1) QLO2(b0,0)
+ }
+ IDATACLC3 MOV1(b0,b1) D2(4) LO1 ADJDITHER2(d2,e2) D3(2)
+
+ HI1 D1(1) QLO2(b0, 7) LDSCL4(b1,O0) D2(4) LO1 PRESCALEA2(d0) D3(2)
+ HI1 D1(1) QLO2(b0, 6) PRESCALEB3(d0) D2(3) LO1 SCALE02(b1,0) D3(2)
+ HI1 D1(1) QLO2(b0, 5) RORSC04(b1,1) D2(4) LO1 RORCLC2(b1) D3(2)
+ HI1 D1(1) QLO2(b0, 4) SCROR04(b1,2) D2(4) LO1 SCALE02(b1,3) D3(2)
+ HI1 D1(1) QLO2(b0, 3) RORSC04(b1,4) D2(4) LO1 RORCLC2(b1) D3(2)
+ HI1 D1(1) QLO2(b0, 2) SCROR04(b1,5) D2(4) LO1 SCALE02(b1,6) D3(2)
+ HI1 D1(1) QLO2(b0, 1) RORSC04(b1,7) D2(4) LO1 RORCLC2(b1) D3(2)
+ HI1 D1(1) QLO2(b0, 0)
+ switch(XTRA0) {
+ case 4: D2(0) LO1 D3(0) HI1 D1(1) QLO2(b0,0)
+ case 3: D2(0) LO1 D3(0) HI1 D1(1) QLO2(b0,0)
+ case 2: D2(0) LO1 D3(0) HI1 D1(1) QLO2(b0,0)
+ case 1: D2(0) LO1 D3(0) HI1 D1(1) QLO2(b0,0)
+ }
+ ADJDITHER2(d0,e0) MOV1(b0,b1) D2(3) LO1 D3(6)
+ ENDLOOP5
+#else
+ // no inline scaling - non-straight RGB ordering -- no longer in line with the actual asm macros above, left for
+ // reference only
+ HI1 D1(1) QLO2(b0, 7) LD2(b1,O1) D2(2) LO1 D3(0)
+ HI1 D1(1) QLO2(b0, 6) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b0, 5) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b0, 4) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b0, 3) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b0, 2) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b0, 1) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b0, 0) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 7) LD2(b1,O2) D2(2) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 6) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 5) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 4) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 3) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 2) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 1) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 0) IDATA2 D2(2) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 7) LD2(b0,O0) D2(2) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 6) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 5) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 4) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 3) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 2) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 1) D2(0) LO1 D3(0)
+ HI1 D1(1) QLO2(b1, 0) D2(0) LO1 D3(0)
+#endif
+
+ // #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ // // set the counter mark
+ // OCR0B = (uint8_t)(TCNT0 + ((WAIT_TIME-INTERRUPT_THRESHOLD)/US_PER_TICK));
+ // TIFR0 = 0x04;
+ // sei();
+ // #endif
+ }
+ DONE;
+ }
+
+ #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ // stop using the clock juggler
+ TCCR0A &= ~0x30;
+ #endif
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void showARGB(struct CARGB *data, int nLeds) {
+ // TODO: IMPLEMENTME
+ }
+#endif
+};
+
+#endif
+
+FASTLED_NAMESPACE_END
+
+#endif
diff --git a/platforms/avr/fastled_avr.h b/platforms/avr/fastled_avr.h
new file mode 100644
index 00000000..f5b5ee58
--- /dev/null
+++ b/platforms/avr/fastled_avr.h
@@ -0,0 +1,14 @@
+#ifndef __INC_FASTLED_AVR_H
+#define __INC_FASTLED_AVR_H
+
+#include "fastled_delay.h"
+#include "fastpin_avr.h"
+#include "fastspi_avr.h"
+#include "clockless_trinket.h"
+
+// Default to using PROGMEM
+#ifndef FASTLED_USE_PROGMEM
+#define FASTLED_USE_PROGMEM 1
+#endif
+
+#endif
diff --git a/fastpin_avr.h b/platforms/avr/fastpin_avr.h
index d606489f..e92f163a 100644
--- a/fastpin_avr.h
+++ b/platforms/avr/fastpin_avr.h
@@ -1,20 +1,24 @@
#ifndef __INC_FASTPIN_AVR_H
#define __INC_FASTPIN_AVR_H
-#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
-#define AVR_PIN_CYCLES(_PIN) (((_PIN >= 62 ) || (_PIN>=42 && _PIN<=49) || (_PIN>=14 && _PIN <=17) || (_PIN>=6 && _PIN <=9)) ? 2 : 1)
+FASTLED_NAMESPACE_BEGIN
+
+#if defined(FASTLED_FORCE_SOFTWARE_PINS)
+#warning "Software pin support forced, pin access will be sloightly slower."
+#define NO_HARDWARE_PIN_SUPPORT
+#undef HAS_HARDWARE_PIN_SUPPORT
+
#else
-#define AVR_PIN_CYCLES(_PIN) ((_PIN >= 24) ? 2 : 1)
-#endif
+#define AVR_PIN_CYCLES(_PIN) ((((int)FastPin<_PIN>::port())-0x20 < 64) ? 1 : 2)
/// Class definition for a Pin where we know the port registers at compile time for said pin. This allows us to make
-/// a lot of optimizations, as the inlined hi/lo methods will devolve to a single io register write/bitset.
-template<uint8_t PIN, uint8_t _MASK, typename _PORT, typename _DDR, typename _PIN> class _AVRPIN {
+/// a lot of optimizations, as the inlined hi/lo methods will devolve to a single io register write/bitset.
+template<uint8_t PIN, uint8_t _MASK, typename _PORT, typename _DDR, typename _PIN> class _AVRPIN {
public:
typedef volatile uint8_t * port_ptr_t;
typedef uint8_t port_t;
-
+
inline static void setOutput() { _DDR::r() |= _MASK; }
inline static void setInput() { _DDR::r() &= ~_MASK; }
@@ -23,7 +27,7 @@ public:
inline static void set(register uint8_t val) __attribute__ ((always_inline)) { _PORT::r() = val; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
-
+
inline static void toggle() __attribute__ ((always_inline)) { _PIN::r() = _MASK; }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
@@ -50,21 +54,77 @@ typedef volatile uint8_t & reg8_t;
#if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__)
_IO(B);
+#define MAX_PIN 5
+
_DEFPIN_AVR(0, 0x01, B); _DEFPIN_AVR(1, 0x02, B); _DEFPIN_AVR(2, 0x04, B); _DEFPIN_AVR(3, 0x08, B);
_DEFPIN_AVR(4, 0x10, B); _DEFPIN_AVR(5, 0x20, B);
#define HAS_HARDWARE_PIN_SUPPORT 1
-#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny25__)
+#elif defined(ARDUINO_AVR_DIGISPARK) // digispark pin layout
+#define MAX_PIN 5
+#define HAS_HARDWARE_PIN_SUPPORT 1
+_IO(A); _IO(B);
+
+_DEFPIN_AVR(0, 0x01, B); _DEFPIN_AVR(1, 0x02, B); _DEFPIN_AVR(2, 0x04, B);
+_DEFPIN_AVR(3, 0x80, A); _DEFPIN_AVR(4, 0x40, A); _DEFPIN_AVR(5, 0x20, A);
+
+#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny25__)
_IO(A); _IO(B);
+#define MAX_PIN 10
+
_DEFPIN_AVR(0, 0x01, A); _DEFPIN_AVR(1, 0x02, A); _DEFPIN_AVR(2, 0x04, A); _DEFPIN_AVR(3, 0x08, A);
_DEFPIN_AVR(4, 0x10, A); _DEFPIN_AVR(5, 0x20, A); _DEFPIN_AVR(6, 0x40, A); _DEFPIN_AVR(7, 0x80, A);
_DEFPIN_AVR(8, 0x04, B); _DEFPIN_AVR(9, 0x02, B); _DEFPIN_AVR(10, 0x01, B);
#define HAS_HARDWARE_PIN_SUPPORT 1
-#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
+#elif defined(ARDUINO_AVR_DIGISPARKPRO)
+
+_IO(A); _IO(B);
+#define MAX_PIN 12
+
+_DEFPIN_AVR(0, 0x01, B); _DEFPIN_AVR(1, 0x02, B); _DEFPIN_AVR(2, 0x04, B); _DEFPIN_AVR(3, 0x20, B);
+_DEFPIN_AVR(4, 0x08, B); _DEFPIN_AVR(5, 0x80, A); _DEFPIN_AVR(6, 0x01, A); _DEFPIN_AVR(7, 0x02, A);
+_DEFPIN_AVR(8, 0x04, A); _DEFPIN_AVR(9, 0x08, A); _DEFPIN_AVR(10, 0x10, A); _DEFPIN_AVR(11, 0x20, A);
+_DEFPIN_AVR(12, 0x40, A);
+
+#elif defined(__AVR_ATtiny167__) || defined(__AVR_ATtiny87__)
+_IO(A); _IO(B);
+
+#define MAX_PIN 15
+
+_DEFPIN_AVR(0, 0x01, A); _DEFPIN_AVR(1, 0x02, A); _DEFPIN_AVR(2, 0x04, A); _DEFPIN_AVR(3, 0x08, A);
+_DEFPIN_AVR(4, 0x10, A); _DEFPIN_AVR(5, 0x20, A); _DEFPIN_AVR(6, 0x40, A); _DEFPIN_AVR(7, 0x80, A);
+_DEFPIN_AVR(8, 0x01, B); _DEFPIN_AVR(9, 0x02, B); _DEFPIN_AVR(10, 0x04, B); _DEFPIN_AVR(11, 0x08, B);
+_DEFPIN_AVR(12, 0x10, B); _DEFPIN_AVR(13, 0x20, B); _DEFPIN_AVR(14, 0x40, B); _DEFPIN_AVR(15, 0x80, B);
+
+#define SPI_DATA 4
+#define SPI_CLOCK 5
+#define AVR_HARDWARE_SPI 1
+
+#define HAS_HARDWARE_PIN_SUPPORT 1
+#elif defined(ARDUINO_HOODLOADER2) && (defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega8U2__)) || defined(__AVR_AT90USB82__) || defined(__AVR_AT90USB162__)
+
+_IO(D); _IO(B); _IO(C);
+
+#define MAX_PIN 20
+
+_DEFPIN_AVR( 0, 0x01, B); _DEFPIN_AVR( 1, 0x02, B); _DEFPIN_AVR( 2, 0x04, B); _DEFPIN_AVR( 3, 0x08, B);
+_DEFPIN_AVR( 4, 0x10, B); _DEFPIN_AVR( 5, 0x20, B); _DEFPIN_AVR( 6, 0x40, B); _DEFPIN_AVR( 7, 0x80, B);
+
+_DEFPIN_AVR( 8, 0x80, C); _DEFPIN_AVR( 9, 0x40, C); _DEFPIN_AVR( 10, 0x20,C); _DEFPIN_AVR( 11, 0x10, C);
+_DEFPIN_AVR( 12, 0x04, C); _DEFPIN_AVR( 13, 0x01, D); _DEFPIN_AVR( 14, 0x02, D); _DEFPIN_AVR(15, 0x04, D);
+_DEFPIN_AVR( 16, 0x08, D); _DEFPIN_AVR( 17, 0x10, D); _DEFPIN_AVR( 18, 0x20, D); _DEFPIN_AVR( 19, 0x40, D);
+_DEFPIN_AVR( 20, 0x80, D);
+
+#define HAS_HARDWARE_PIN_SUPPORT 1
+// #define SPI_DATA 2
+// #define SPI_CLOCK 1
+// #define AVR_HARDWARE_SPI 1
+
+#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega8__)
// Accelerated port definitions for arduino avrs
_IO(D); _IO(B); _IO(C);
@@ -81,6 +141,9 @@ _DEFPIN_AVR(16, 0x04, C); _DEFPIN_AVR(17, 0x08, C); _DEFPIN_AVR(18, 0x10, C); _D
#define AVR_HARDWARE_SPI 1
#define HAS_HARDWARE_PIN_SUPPORT 1
+#define SPI_UART0_DATA 1
+#define SPI_UART0_CLOCK 4
+
#elif defined(__AVR_ATmega1284P__)
_IO(A); _IO(B); _IO(C); _IO(D);
@@ -106,24 +169,24 @@ _DEFPIN_AVR(28, 1<<6, C); _DEFPIN_AVR(29, 1<<7, C); _DEFPIN_AVR(30, 1<<4, D); _D
_IO(A); _IO(B); _IO(C); _IO(D); _IO(E); _IO(F); _IO(G); _IO(H); _IO(J); _IO(K); _IO(L);
#define MAX_PIN 69
-_DEFPIN_AVR(0, 1, E); _DEFPIN_AVR(1, 2, E); _DEFPIN_AVR(2, 16, E); _DEFPIN_AVR(3, 32, E);
-_DEFPIN_AVR(4, 32, G); _DEFPIN_AVR(5, 8, E); _DEFPIN_AVR(6, 8, H); _DEFPIN_AVR(7, 16, H);
-_DEFPIN_AVR(8, 32, H); _DEFPIN_AVR(9, 64, H); _DEFPIN_AVR(10, 16, B); _DEFPIN_AVR(11, 32, B);
-_DEFPIN_AVR(12, 64, B); _DEFPIN_AVR(13, 128, B); _DEFPIN_AVR(14, 2, J); _DEFPIN_AVR(15, 1, J);
-_DEFPIN_AVR(16, 2, H); _DEFPIN_AVR(17, 1, H); _DEFPIN_AVR(18, 8, D); _DEFPIN_AVR(19, 4, D);
-_DEFPIN_AVR(20, 2, D); _DEFPIN_AVR(21, 1, D); _DEFPIN_AVR(22, 1, A); _DEFPIN_AVR(23, 2, A);
-_DEFPIN_AVR(24, 4, A); _DEFPIN_AVR(25, 8, A); _DEFPIN_AVR(26, 16, A); _DEFPIN_AVR(27, 32, A);
-_DEFPIN_AVR(28, 64, A); _DEFPIN_AVR(29, 128, A); _DEFPIN_AVR(30, 128, C); _DEFPIN_AVR(31, 64, C);
-_DEFPIN_AVR(32, 32, C); _DEFPIN_AVR(33, 16, C); _DEFPIN_AVR(34, 8, C); _DEFPIN_AVR(35, 4, C);
-_DEFPIN_AVR(36, 2, C); _DEFPIN_AVR(37, 1, C); _DEFPIN_AVR(38, 128, D); _DEFPIN_AVR(39, 4, G);
-_DEFPIN_AVR(40, 2, G); _DEFPIN_AVR(41, 1, G); _DEFPIN_AVR(42, 128, L); _DEFPIN_AVR(43, 64, L);
-_DEFPIN_AVR(44, 32, L); _DEFPIN_AVR(45, 16, L); _DEFPIN_AVR(46, 8, L); _DEFPIN_AVR(47, 4, L);
-_DEFPIN_AVR(48, 2, L); _DEFPIN_AVR(49, 1, L); _DEFPIN_AVR(50, 8, B); _DEFPIN_AVR(51, 4, B);
-_DEFPIN_AVR(52, 2, B); _DEFPIN_AVR(53, 1, B); _DEFPIN_AVR(54, 1, F); _DEFPIN_AVR(55, 2, F);
-_DEFPIN_AVR(56, 4, F); _DEFPIN_AVR(57, 8, F); _DEFPIN_AVR(58, 16, F); _DEFPIN_AVR(59, 32, F);
-_DEFPIN_AVR(60, 64, F); _DEFPIN_AVR(61, 128, F); _DEFPIN_AVR(62, 1, K); _DEFPIN_AVR(63, 2, K);
-_DEFPIN_AVR(64, 4, K); _DEFPIN_AVR(65, 8, K); _DEFPIN_AVR(66, 16, K); _DEFPIN_AVR(67, 32, K);
-_DEFPIN_AVR(68, 64, K); _DEFPIN_AVR(69, 128, K);
+_DEFPIN_AVR(0, 1, E); _DEFPIN_AVR(1, 2, E); _DEFPIN_AVR(2, 16, E); _DEFPIN_AVR(3, 32, E);
+_DEFPIN_AVR(4, 32, G); _DEFPIN_AVR(5, 8, E); _DEFPIN_AVR(6, 8, H); _DEFPIN_AVR(7, 16, H);
+_DEFPIN_AVR(8, 32, H); _DEFPIN_AVR(9, 64, H); _DEFPIN_AVR(10, 16, B); _DEFPIN_AVR(11, 32, B);
+_DEFPIN_AVR(12, 64, B); _DEFPIN_AVR(13, 128, B); _DEFPIN_AVR(14, 2, J); _DEFPIN_AVR(15, 1, J);
+_DEFPIN_AVR(16, 2, H); _DEFPIN_AVR(17, 1, H); _DEFPIN_AVR(18, 8, D); _DEFPIN_AVR(19, 4, D);
+_DEFPIN_AVR(20, 2, D); _DEFPIN_AVR(21, 1, D); _DEFPIN_AVR(22, 1, A); _DEFPIN_AVR(23, 2, A);
+_DEFPIN_AVR(24, 4, A); _DEFPIN_AVR(25, 8, A); _DEFPIN_AVR(26, 16, A); _DEFPIN_AVR(27, 32, A);
+_DEFPIN_AVR(28, 64, A); _DEFPIN_AVR(29, 128, A); _DEFPIN_AVR(30, 128, C); _DEFPIN_AVR(31, 64, C);
+_DEFPIN_AVR(32, 32, C); _DEFPIN_AVR(33, 16, C); _DEFPIN_AVR(34, 8, C); _DEFPIN_AVR(35, 4, C);
+_DEFPIN_AVR(36, 2, C); _DEFPIN_AVR(37, 1, C); _DEFPIN_AVR(38, 128, D); _DEFPIN_AVR(39, 4, G);
+_DEFPIN_AVR(40, 2, G); _DEFPIN_AVR(41, 1, G); _DEFPIN_AVR(42, 128, L); _DEFPIN_AVR(43, 64, L);
+_DEFPIN_AVR(44, 32, L); _DEFPIN_AVR(45, 16, L); _DEFPIN_AVR(46, 8, L); _DEFPIN_AVR(47, 4, L);
+_DEFPIN_AVR(48, 2, L); _DEFPIN_AVR(49, 1, L); _DEFPIN_AVR(50, 8, B); _DEFPIN_AVR(51, 4, B);
+_DEFPIN_AVR(52, 2, B); _DEFPIN_AVR(53, 1, B); _DEFPIN_AVR(54, 1, F); _DEFPIN_AVR(55, 2, F);
+_DEFPIN_AVR(56, 4, F); _DEFPIN_AVR(57, 8, F); _DEFPIN_AVR(58, 16, F); _DEFPIN_AVR(59, 32, F);
+_DEFPIN_AVR(60, 64, F); _DEFPIN_AVR(61, 128, F); _DEFPIN_AVR(62, 1, K); _DEFPIN_AVR(63, 2, K);
+_DEFPIN_AVR(64, 4, K); _DEFPIN_AVR(65, 8, K); _DEFPIN_AVR(66, 16, K); _DEFPIN_AVR(67, 32, K);
+_DEFPIN_AVR(68, 64, K); _DEFPIN_AVR(69, 128, K);
#define SPI_DATA 51
#define SPI_CLOCK 52
@@ -135,15 +198,15 @@ _DEFPIN_AVR(68, 64, K); _DEFPIN_AVR(69, 128, K);
#elif defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY)
// teensy defs
-_IO(B); _IO(C); _IO(D); _IO(E); _IO(F);
+_IO(B); _IO(C); _IO(D); _IO(E); _IO(F);
#define MAX_PIN 23
-_DEFPIN_AVR(0, 1, B); _DEFPIN_AVR(1, 2, B); _DEFPIN_AVR(2, 4, B); _DEFPIN_AVR(3, 8, B);
-_DEFPIN_AVR(4, 128, B); _DEFPIN_AVR(5, 1, D); _DEFPIN_AVR(6, 2, D); _DEFPIN_AVR(7, 4, D);
-_DEFPIN_AVR(8, 8, D); _DEFPIN_AVR(9, 64, C); _DEFPIN_AVR(10, 128, C); _DEFPIN_AVR(11, 64, D);
-_DEFPIN_AVR(12, 128, D); _DEFPIN_AVR(13, 16, B); _DEFPIN_AVR(14, 32, B); _DEFPIN_AVR(15, 64, B);
-_DEFPIN_AVR(16, 128, F); _DEFPIN_AVR(17, 64, F); _DEFPIN_AVR(18, 32, F); _DEFPIN_AVR(19, 16, F);
-_DEFPIN_AVR(20, 2, F); _DEFPIN_AVR(21, 1, F); _DEFPIN_AVR(22, 16, D); _DEFPIN_AVR(23, 32, D);
+_DEFPIN_AVR(0, 1, B); _DEFPIN_AVR(1, 2, B); _DEFPIN_AVR(2, 4, B); _DEFPIN_AVR(3, 8, B);
+_DEFPIN_AVR(4, 128, B); _DEFPIN_AVR(5, 1, D); _DEFPIN_AVR(6, 2, D); _DEFPIN_AVR(7, 4, D);
+_DEFPIN_AVR(8, 8, D); _DEFPIN_AVR(9, 64, C); _DEFPIN_AVR(10, 128, C); _DEFPIN_AVR(11, 64, D);
+_DEFPIN_AVR(12, 128, D); _DEFPIN_AVR(13, 16, B); _DEFPIN_AVR(14, 32, B); _DEFPIN_AVR(15, 64, B);
+_DEFPIN_AVR(16, 128, F); _DEFPIN_AVR(17, 64, F); _DEFPIN_AVR(18, 32, F); _DEFPIN_AVR(19, 16, F);
+_DEFPIN_AVR(20, 2, F); _DEFPIN_AVR(21, 1, F); _DEFPIN_AVR(22, 16, D); _DEFPIN_AVR(23, 32, D);
#define SPI_DATA 2
#define SPI_CLOCK 1
@@ -151,24 +214,28 @@ _DEFPIN_AVR(20, 2, F); _DEFPIN_AVR(21, 1, F); _DEFPIN_AVR(22, 16, D); _DEFPIN_AV
#define AVR_HARDWARE_SPI 1
#define HAS_HARDWARE_PIN_SUPPORT 1
+// PD3/PD5
+#define SPI_UART1_DATA 8
+#define SPI_UART1_CLOCK 23
+
#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
// teensy++ 2 defs
-_IO(A); _IO(B); _IO(C); _IO(D); _IO(E); _IO(F);
+_IO(A); _IO(B); _IO(C); _IO(D); _IO(E); _IO(F);
#define MAX_PIN 45
-_DEFPIN_AVR(0, 1, D); _DEFPIN_AVR(1, 2, D); _DEFPIN_AVR(2, 4, D); _DEFPIN_AVR(3, 8, D);
-_DEFPIN_AVR(4, 16, D); _DEFPIN_AVR(5, 32, D); _DEFPIN_AVR(6, 64, D); _DEFPIN_AVR(7, 128, D);
-_DEFPIN_AVR(8, 1, E); _DEFPIN_AVR(9, 2, E); _DEFPIN_AVR(10, 1, C); _DEFPIN_AVR(11, 2, C);
-_DEFPIN_AVR(12, 4, C); _DEFPIN_AVR(13, 8, C); _DEFPIN_AVR(14, 16, C); _DEFPIN_AVR(15, 32, C);
-_DEFPIN_AVR(16, 64, C); _DEFPIN_AVR(17, 128, C); _DEFPIN_AVR(18, 64, E); _DEFPIN_AVR(19, 128, E);
-_DEFPIN_AVR(20, 1, B); _DEFPIN_AVR(21, 2, B); _DEFPIN_AVR(22, 4, B); _DEFPIN_AVR(23, 8, B);
-_DEFPIN_AVR(24, 16, B); _DEFPIN_AVR(25, 32, B); _DEFPIN_AVR(26, 64, B); _DEFPIN_AVR(27, 128, B);
-_DEFPIN_AVR(28, 1, A); _DEFPIN_AVR(29, 2, A); _DEFPIN_AVR(30, 4, A); _DEFPIN_AVR(31, 8, A);
-_DEFPIN_AVR(32, 16, A); _DEFPIN_AVR(33, 32, A); _DEFPIN_AVR(34, 64, A); _DEFPIN_AVR(35, 128, A);
-_DEFPIN_AVR(36, 16, E); _DEFPIN_AVR(37, 32, E); _DEFPIN_AVR(38, 1, F); _DEFPIN_AVR(39, 2, F);
-_DEFPIN_AVR(40, 4, F); _DEFPIN_AVR(41, 8, F); _DEFPIN_AVR(42, 16, F); _DEFPIN_AVR(43, 32, F);
-_DEFPIN_AVR(44, 64, F); _DEFPIN_AVR(45, 128, F);
+_DEFPIN_AVR(0, 1, D); _DEFPIN_AVR(1, 2, D); _DEFPIN_AVR(2, 4, D); _DEFPIN_AVR(3, 8, D);
+_DEFPIN_AVR(4, 16, D); _DEFPIN_AVR(5, 32, D); _DEFPIN_AVR(6, 64, D); _DEFPIN_AVR(7, 128, D);
+_DEFPIN_AVR(8, 1, E); _DEFPIN_AVR(9, 2, E); _DEFPIN_AVR(10, 1, C); _DEFPIN_AVR(11, 2, C);
+_DEFPIN_AVR(12, 4, C); _DEFPIN_AVR(13, 8, C); _DEFPIN_AVR(14, 16, C); _DEFPIN_AVR(15, 32, C);
+_DEFPIN_AVR(16, 64, C); _DEFPIN_AVR(17, 128, C); _DEFPIN_AVR(18, 64, E); _DEFPIN_AVR(19, 128, E);
+_DEFPIN_AVR(20, 1, B); _DEFPIN_AVR(21, 2, B); _DEFPIN_AVR(22, 4, B); _DEFPIN_AVR(23, 8, B);
+_DEFPIN_AVR(24, 16, B); _DEFPIN_AVR(25, 32, B); _DEFPIN_AVR(26, 64, B); _DEFPIN_AVR(27, 128, B);
+_DEFPIN_AVR(28, 1, A); _DEFPIN_AVR(29, 2, A); _DEFPIN_AVR(30, 4, A); _DEFPIN_AVR(31, 8, A);
+_DEFPIN_AVR(32, 16, A); _DEFPIN_AVR(33, 32, A); _DEFPIN_AVR(34, 64, A); _DEFPIN_AVR(35, 128, A);
+_DEFPIN_AVR(36, 16, E); _DEFPIN_AVR(37, 32, E); _DEFPIN_AVR(38, 1, F); _DEFPIN_AVR(39, 2, F);
+_DEFPIN_AVR(40, 4, F); _DEFPIN_AVR(41, 8, F); _DEFPIN_AVR(42, 16, F); _DEFPIN_AVR(43, 32, F);
+_DEFPIN_AVR(44, 64, F); _DEFPIN_AVR(45, 128, F);
#define SPI_DATA 22
#define SPI_CLOCK 21
@@ -176,10 +243,15 @@ _DEFPIN_AVR(44, 64, F); _DEFPIN_AVR(45, 128, F);
#define AVR_HARDWARE_SPI 1
#define HAS_HARDWARE_PIN_SUPPORT 1
+// PD3/PD5
+#define SPI_UART1_DATA 3
+#define SPI_UART1_CLOCK 5
+
+
#elif defined(__AVR_ATmega32U4__)
// leonard defs
-_IO(B); _IO(C); _IO(D); _IO(E); _IO(F);
+_IO(B); _IO(C); _IO(D); _IO(E); _IO(F);
#define MAX_PIN 23
_DEFPIN_AVR(0, 4, D); _DEFPIN_AVR(1, 8, D); _DEFPIN_AVR(2, 2, D); _DEFPIN_AVR(3, 1, D);
@@ -194,6 +266,15 @@ _DEFPIN_AVR(20, 32, F); _DEFPIN_AVR(21, 16, F); _DEFPIN_AVR(22, 2, F); _DEFPIN_A
#define AVR_HARDWARE_SPI 1
#define HAS_HARDWARE_PIN_SUPPORT 1
-#endif
+// PD3/PD5
+// #define SPI_UART1_DATA 1
+// #define SPI_UART1_CLOCK 4
+
#endif
+
+#endif // FASTLED_FORCE_SOFTWARE_PINS
+
+FASTLED_NAMESPACE_END
+
+#endif // __INC_FASTPIN_AVR_H
diff --git a/fastspi_avr.h b/platforms/avr/fastspi_avr.h
index 6470f37d..2ed5bb15 100644
--- a/fastspi_avr.h
+++ b/platforms/avr/fastspi_avr.h
@@ -1,6 +1,8 @@
#ifndef __INC_FASTSPI_AVR_H
#define __INC_FASTSPI_AVR_H
+FASTLED_NAMESPACE_BEGIN
+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Hardware SPI support using USART registers and friends
@@ -11,43 +13,73 @@
// uno/mini/duemilanove
#if defined(AVR_HARDWARE_SPI)
-#if defined(UBRR0)
+
+#if defined(UBRR1)
+
+#ifndef UCPHA1
+#define UCPHA1 1
+#endif
+
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
-class AVRUSARTSPIOutput {
+class AVRUSART1SPIOutput {
Selectable *m_pSelect;
public:
- AVRUSARTSPIOutput() { m_pSelect = NULL; }
- AVRUSARTSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
+ AVRUSART1SPIOutput() { m_pSelect = NULL; }
+ AVRUSART1SPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
void init() {
- UBRR0 = 0;
- UCSR0A = 1<<TXC0;
+ UBRR1 = 0;
+
+ /* Set MSPI mode of operation and SPI data mode 0. */
+ UCSR1C = (1<<UMSEL11)|(1<<UMSEL10)|(0<<UCPHA1)|(0<<UCPOL1);
+ /* Enable receiver and transmitter. */
+ UCSR1B = (1<<RXEN1)|(1<<TXEN1);
FastPin<_CLOCK_PIN>::setOutput();
FastPin<_DATA_PIN>::setOutput();
- UCSR0C = _BV (UMSEL00) | _BV (UMSEL01); // Master SPI mode
- UCSR0B = _BV (TXEN0) | _BV (RXEN0); // transmit enable and receive enable
-
// must be done last, see page 206
- UBRR0 = 3; // 2 Mhz clock rate
+ setSPIRate();
}
+ void setSPIRate() {
+ if(_SPI_CLOCK_DIVIDER > 2) {
+ UBRR1 = (_SPI_CLOCK_DIVIDER/2)-1;
+ } else {
+ UBRR1 = 0;
+ }
+ }
+
+
static void stop() {
// TODO: stop the uart spi output
}
- static void wait() __attribute__((always_inline)) { while(!(UCSR0A & (1<<UDRE0))); }
+ static bool shouldWait(bool wait = false) __attribute__((always_inline)) {
+ static bool sWait=false;
+ if(sWait) {
+ sWait = wait; return true;
+ } else {
+ sWait = wait; return false;
+ }
+ // return true;
+ }
+ static void wait() __attribute__((always_inline)) {
+ if(shouldWait()) {
+ while(!(UCSR1A & (1<<UDRE1)));
+ }
+ }
static void waitFully() __attribute__((always_inline)) { wait(); }
- static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { UDR0 = b;}
- static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { UDR0 = b; wait(); }
- static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); UDR0 = b; }
-
static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
+ static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); UDR1=b; shouldWait(true); }
+ static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { UDR1=b; shouldWait(true); wait(); }
+ static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { UDR1=b; shouldWait(true); }
+
+
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
if(b && (1 << BIT)) {
FastPin<_DATA_PIN>::hi();
@@ -59,18 +91,32 @@ public:
FastPin<_CLOCK_PIN>::lo();
}
- void select() { if(m_pSelect != NULL) { m_pSelect->select(); } } // FastPin<_SELECT_PIN>::hi(); }
+ void enable_pins() { }
+ void disable_pins() { }
+
+ void select() {
+ if(m_pSelect != NULL) {
+ m_pSelect->select();
+ }
+ enable_pins();
+ setSPIRate();
+ }
+
void release() {
- // wait for all transmissions to finish
- while ((UCSR0A & (1 <<TXC0)) == 0) {}
- if(m_pSelect != NULL) { m_pSelect->release(); } // FastPin<_SELECT_PIN>::hi();
+ if(m_pSelect != NULL) {
+ m_pSelect->release();
+ }
+ disable_pins();
}
static void writeBytesValueRaw(uint8_t value, int len) {
- while(len--) { writeByte(value); }
+ while(len--) {
+ writeByte(value);
+ }
}
void writeBytesValue(uint8_t value, int len) {
+ //setSPIRate();
select();
while(len--) {
writeByte(value);
@@ -80,13 +126,13 @@ public:
// Write a block of n uint8_ts out
template <class D> void writeBytes(register uint8_t *data, int len) {
+ //setSPIRate();
uint8_t *end = data + len;
select();
while(data != end) {
// a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM)
writeByte(D::adjust(*data++)); delaycycles<3>();
}
- D::postBlock(len);
release();
}
@@ -94,43 +140,182 @@ public:
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
- template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale, bool advance=true, uint8_t skip=0) {
- uint8_t *end = data + len;
- PixelController<RGB_ORDER> pixels(data, scale, true, advance, skip);
+ template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
+ //setSPIRate();
+ int len = pixels.mLen;
+
select();
- while(data != end) {
+ while(pixels.has(1)) {
if(FLAGS & FLAG_START_BIT) {
writeBit<0>(1);
+ writeBytePostWait(D::adjust(pixels.loadAndScale0()));
+ writeBytePostWait(D::adjust(pixels.loadAndScale1()));
+ writeBytePostWait(D::adjust(pixels.loadAndScale2()));
+ } else {
+ writeByte(D::adjust(pixels.loadAndScale0()));
+ writeByte(D::adjust(pixels.loadAndScale1()));
+ writeByte(D::adjust(pixels.loadAndScale2()));
}
- writeByte(D::adjust(pixels.loadAndScale0()));
- writeByte(D::adjust(pixels.loadAndScale1()));
- writeByte(D::adjust(pixels.loadAndScale2()));
+
pixels.advanceData();
pixels.stepDithering();
- data += (3+skip);
}
D::postBlock(len);
release();
}
+};
+#endif
+
+#if defined(UBRR0)
+template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
+class AVRUSART0SPIOutput {
+ Selectable *m_pSelect;
+
+public:
+ AVRUSART0SPIOutput() { m_pSelect = NULL; }
+ AVRUSART0SPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
+ void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
+
+ void init() {
+ UBRR0 = 0;
+
+ /* Set MSPI mode of operation and SPI data mode 0. */
+ UCSR0C = (1<<UMSEL01)|(1<<UMSEL00)/*|(0<<UCPHA0)*/|(0<<UCPOL0);
+ /* Enable receiver and transmitter. */
+ UCSR0B = (1<<RXEN0)|(1<<TXEN0);
+
+ FastPin<_CLOCK_PIN>::setOutput();
+ FastPin<_DATA_PIN>::setOutput();
+
+
+ // must be done last, see page 206
+ setSPIRate();
+ }
+
+ void setSPIRate() {
+ if(_SPI_CLOCK_DIVIDER > 2) {
+ UBRR0 = (_SPI_CLOCK_DIVIDER/2)-1;
+ } else {
+ UBRR0 = 0;
+ }
+ }
+
+ static void stop() {
+ // TODO: stop the uart spi output
+ }
+
+ static bool shouldWait(bool wait = false) __attribute__((always_inline)) {
+ static bool sWait=false;
+ if(sWait) {
+ sWait = wait; return true;
+ } else {
+ sWait = wait; return false;
+ }
+ // return true;
+ }
+ static void wait() __attribute__((always_inline)) {
+ if(shouldWait()) {
+ while(!(UCSR0A & (1<<UDRE0)));
+ }
+ }
+ static void waitFully() __attribute__((always_inline)) { wait(); }
+
+ static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
+
+ static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); UDR0=b; shouldWait(true); }
+ static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { UDR0=b; shouldWait(true); wait(); }
+ static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { UDR0=b; shouldWait(true); }
- // template instantiations for writeBytes 3
- template <uint8_t FLAGS, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale, bool advance=true, uint8_t skip=0) {
- writeBytes3<FLAGS, DATA_NOP, RGB_ORDER>(data, len, scale, advance, skip);
+
+ template <uint8_t BIT> inline static void writeBit(uint8_t b) {
+ if(b && (1 << BIT)) {
+ FastPin<_DATA_PIN>::hi();
+ } else {
+ FastPin<_DATA_PIN>::lo();
+ }
+
+ FastPin<_CLOCK_PIN>::hi();
+ FastPin<_CLOCK_PIN>::lo();
}
- template <class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale, bool advance=true, uint8_t skip=0) {
- writeBytes3<0, D, RGB_ORDER>(data, len, scale, advance, skip);
+
+ void enable_pins() { }
+ void disable_pins() { }
+
+ void select() {
+ if(m_pSelect != NULL) {
+ m_pSelect->select();
+ }
+ enable_pins();
+ setSPIRate();
}
- template <EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale, bool advance=true, uint8_t skip=0) {
- writeBytes3<0, DATA_NOP, RGB_ORDER>(data, len, scale, advance, skip);
+
+ void release() {
+ if(m_pSelect != NULL) {
+ m_pSelect->release();
+ }
+ disable_pins();
+ }
+
+ static void writeBytesValueRaw(uint8_t value, int len) {
+ while(len--) {
+ writeByte(value);
+ }
}
- void writeBytes3(register uint8_t *data, int len, register uint8_t scale, bool advance=true, uint8_t skip=0) {
- writeBytes3<0, DATA_NOP, RGB>(data, len, scale, advance, skip);
+
+ void writeBytesValue(uint8_t value, int len) {
+ //setSPIRate();
+ select();
+ while(len--) {
+ writeByte(value);
+ }
+ release();
}
+ // Write a block of n uint8_ts out
+ template <class D> void writeBytes(register uint8_t *data, int len) {
+ //setSPIRate();
+ uint8_t *end = data + len;
+ select();
+ while(data != end) {
+ // a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM)
+ writeByte(D::adjust(*data++)); delaycycles<3>();
+ }
+ release();
+ }
+
+ void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
+
+ // write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
+ // parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
+ template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
+ //setSPIRate();
+ int len = pixels.mLen;
+
+ select();
+ while(pixels.has(1)) {
+ if(FLAGS & FLAG_START_BIT) {
+ writeBit<0>(1);
+ writeBytePostWait(D::adjust(pixels.loadAndScale0()));
+ writeBytePostWait(D::adjust(pixels.loadAndScale1()));
+ writeBytePostWait(D::adjust(pixels.loadAndScale2()));
+ } else {
+ writeByte(D::adjust(pixels.loadAndScale0()));
+ writeByte(D::adjust(pixels.loadAndScale1()));
+ writeByte(D::adjust(pixels.loadAndScale2()));
+ }
+
+ pixels.advanceData();
+ pixels.stepDithering();
+ }
+ D::postBlock(len);
+ waitFully();
+ release();
+ }
};
#endif
+
#if defined(SPSR)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -181,7 +366,6 @@ public:
FastPin<SPI_SELECT>::setOutput();
FastPin<SPI_SELECT>::lo();
#endif
- release();
SPCR |= ((1<<SPE) | (1<<MSTR) ); // enable SPI as master
SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear out the prescalar bits
@@ -205,7 +389,8 @@ public:
SPDR=0;
shouldWait(false);
- }
+ release();
+ }
static bool shouldWait(bool wait = false) __attribute__((always_inline)) {
static bool sWait=false;
@@ -235,8 +420,24 @@ public:
shouldWait(false);
}
- void select() { if(m_pSelect != NULL) { m_pSelect->select(); } } // FastPin<_SELECT_PIN>::hi(); }
- void release() { if(m_pSelect != NULL) { m_pSelect->release(); } } // FastPin<_SELECT_PIN>::lo(); }
+ void enable_pins() {
+ SPCR |= ((1<<SPE) | (1<<MSTR) ); // enable SPI as master
+ }
+
+ void disable_pins() {
+ SPCR &= ~(((1<<SPE) | (1<<MSTR) )); // disable SPI
+ }
+
+ void select() {
+ if(m_pSelect != NULL) { m_pSelect->select(); }
+ enable_pins();
+ setSPIRate();
+ }
+
+ void release() {
+ if(m_pSelect != NULL) { m_pSelect->release(); }
+ disable_pins();
+ }
static void writeBytesValueRaw(uint8_t value, int len) {
while(len--) { writeByte(value); }
@@ -288,13 +489,17 @@ public:
pixels.stepDithering();
}
D::postBlock(len);
+ waitFully();
release();
}
};
#endif
#else
-// #define FORCE_SOFTWARE_SPI
+// #define FASTLED_FORCE_SOFTWARE_SPI
#endif
+FASTLED_NAMESPACE_END;
+
+
#endif
diff --git a/platforms/avr/led_sysdefs_avr.h b/platforms/avr/led_sysdefs_avr.h
new file mode 100644
index 00000000..d90ee6e3
--- /dev/null
+++ b/platforms/avr/led_sysdefs_avr.h
@@ -0,0 +1,52 @@
+#ifndef __INC_LED_SYSDEFS_AVR_H
+#define __INC_LED_SYSDEFS_AVR_H
+
+#define FASTLED_AVR
+
+#ifndef INTERRUPT_THRESHOLD
+#define INTERRUPT_THRESHOLD 2
+#endif
+
+#include <avr/io.h>
+#include <avr/interrupt.h> // for cli/se definitions
+
+// Define the register types
+#if defined(ARDUINO) // && ARDUINO < 150
+typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
+typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
+#endif
+
+
+// Default to disallowing interrupts (may want to gate this on teensy2 vs. other arm platforms, since the
+// teensy2 has a good, fast millis interrupt implementation)
+#ifndef FASTLED_ALLOW_INTERRUPTS
+#define FASTLED_ALLOW_INTERRUPTS 0
+#endif
+
+#if FASTLED_ALLOW_INTERRUPTS == 1
+#define FASTLED_ACCURATE_CLOCK
+#endif
+
+
+// Default to using PROGMEM here
+#ifndef FASTLED_USE_PROGMEM
+#define FASTLED_USE_PROGMEM 1
+#endif
+
+#if defined(ARDUINO_AVR_DIGISPARK) || defined(ARDUINO_AVR_DIGISPARKPRO)
+#ifndef NO_CORRECTION
+#define NO_CORRECTION 1
+#endif
+#endif
+
+extern "C" {
+# if defined(CORE_TEENSY) || defined(TEENSYDUINO)
+extern volatile unsigned long timer0_millis_count;
+# define MS_COUNTER timer0_millis_count
+# else
+extern volatile unsigned long timer0_millis;
+# define MS_COUNTER timer0_millis
+# endif
+};
+
+#endif
diff --git a/power_mgt.cpp b/power_mgt.cpp
index 46befd6e..c4cbf548 100644
--- a/power_mgt.cpp
+++ b/power_mgt.cpp
@@ -1,6 +1,9 @@
+#define FASTLED_INTERNAL
#include "FastLED.h"
#include "power_mgt.h"
+FASTLED_NAMESPACE_BEGIN
+
//// POWER MANAGEMENT
// These power usage values are approximate, and your exact readings
@@ -51,9 +54,9 @@ uint32_t calculate_unscaled_power_mW( const CRGB* ledbuffer, uint16_t numLeds )
uint32_t red32 = 0, green32 = 0, blue32 = 0;
const CRGB* firstled = &(ledbuffer[0]);
uint8_t* p = (uint8_t*)(firstled);
-
+
uint16_t count = numLeds;
-
+
// This loop might benefit from an AVR assembly version -MEK
while( count) {
red32 += *p++;
@@ -61,17 +64,17 @@ uint32_t calculate_unscaled_power_mW( const CRGB* ledbuffer, uint16_t numLeds )
blue32 += *p++;
count--;
}
-
+
red32 *= gRed_mW;
green32 *= gGreen_mW;
blue32 *= gBlue_mW;
-
+
red32 >>= 8;
green32 >>= 8;
blue32 >>= 8;
-
+
uint32_t total = red32 + green32 + blue32 + (gDark_mW * numLeds);
-
+
return total;
}
@@ -83,7 +86,7 @@ uint32_t calculate_unscaled_power_mW( const CRGB* ledbuffer, uint16_t numLeds )
uint8_t calculate_max_brightness_for_power_mW( uint8_t target_brightness, uint32_t max_power_mW)
{
uint32_t total_mW = gMCU_mW;
-
+
CLEDController *pCur = CLEDController::head();
while(pCur) {
total_mW += calculate_unscaled_power_mW( pCur->leds(), pCur->size());
@@ -94,7 +97,7 @@ uint8_t calculate_max_brightness_for_power_mW( uint8_t target_brightness, uint32
Serial.print("power demand at full brightness mW = ");
Serial.println( total_mW);
#endif
-
+
uint32_t requested_power_mW = ((uint32_t)total_mW * target_brightness) / 256;
#if POWER_DEBUG_PRINT == 1
if( target_brightness != 255 ) {
@@ -104,7 +107,7 @@ uint8_t calculate_max_brightness_for_power_mW( uint8_t target_brightness, uint32
Serial.print("power limit mW = ");
Serial.println( max_power_mW);
#endif
-
+
if( requested_power_mW < max_power_mW) {
#if POWER_LED > 0
if( gMaxPowerIndicatorLEDPinNumber ) {
@@ -116,7 +119,7 @@ uint8_t calculate_max_brightness_for_power_mW( uint8_t target_brightness, uint32
#endif
return target_brightness;
}
-
+
uint8_t recommended_brightness = (uint32_t)((uint8_t)(target_brightness) * (uint32_t)(max_power_mW)) / ((uint32_t)(requested_power_mW));
#if POWER_DEBUG_PRINT == 1
Serial.print("recommended brightness # = ");
@@ -125,16 +128,16 @@ uint8_t calculate_max_brightness_for_power_mW( uint8_t target_brightness, uint32
uint32_t resultant_power_mW = (total_mW * recommended_brightness) / 256;
Serial.print("resultant power demand mW = ");
Serial.println( resultant_power_mW);
-
+
Serial.println();
#endif
-
+
#if POWER_LED > 0
if( gMaxPowerIndicatorLEDPinNumber ) {
digitalWrite( gMaxPowerIndicatorLEDPinNumber, HIGH); // turn the LED on
}
#endif
-
+
return recommended_brightness;
}
@@ -158,7 +161,7 @@ void show_at_max_brightness_for_power()
{
uint8_t targetBrightness = FastLED.getBrightness();
uint8_t max = calculate_max_brightness_for_power_mW( targetBrightness, gMaxPowerInMilliwatts);
-
+
FastLED.setBrightness( max );
FastLED.show();
FastLED.setBrightness( targetBrightness );
@@ -168,9 +171,10 @@ void delay_at_max_brightness_for_power( uint16_t ms)
{
uint8_t targetBrightness = FastLED.getBrightness();
uint8_t max = calculate_max_brightness_for_power_mW( targetBrightness, gMaxPowerInMilliwatts);
-
+
FastLED.setBrightness( max );
FastLED.delay( ms);
FastLED.setBrightness( targetBrightness );
}
+FASTLED_NAMESPACE_END
diff --git a/power_mgt.h b/power_mgt.h
index 60b92e95..4672f0ea 100644
--- a/power_mgt.h
+++ b/power_mgt.h
@@ -3,14 +3,24 @@
#include "pixeltypes.h"
+FASTLED_NAMESPACE_BEGIN
+
+///@defgroup Power Power management functions
+/// functions used to limit the amount of power used by FastLED
+
// Power Control setup functions
//
// Example:
// set_max_power_in_volts_and_milliamps( 5, 400);
//
+
+/// Set the maximum power used in milliamps for a given voltage
void set_max_power_in_volts_and_milliamps( uint8_t volts, uint32_t milliamps);
+/// Set the maximum power used in watts
void set_max_power_in_milliwatts( uint32_t powerInmW);
+/// Select a ping with an led that will be flashed to indicate that power management
+/// is pulling down the brightness
void set_max_power_indicator_LED( uint8_t pinNumber); // zero = no indicator LED
@@ -25,24 +35,31 @@ void set_max_power_indicator_LED( uint8_t pinNumber); // zero = no indicator LED
// // now is:
// show_at_max_brightness_for_power();
//
+
+/// Similar to FastLED.show, but pre-adjusts brightness to keep below the power
+/// threshold.
void show_at_max_brightness_for_power();
+/// Similar to FastLED.delay, but pre-adjusts brightness to keep below the power
+/// threshold.
void delay_at_max_brightness_for_power( uint16_t ms);
// Power Control internal helper functions
-//
-// calculate_unscaled_power_mW tells you how many milliwatts the current
-// LED data would draw at brightness = 255.
-//
-// calculate_max_brightness_for_power_mW tells you the highest brightness
-// level you can use and still stay under the specified power budget. It
-// takes a 'target brightness' which is the brightness you'd ideally like
-// to use. The result from this function will be no higher than the
-// target_brightess you supply, but may be lower.
+
+/// calculate_unscaled_power_mW tells you how many milliwatts the current
+/// LED data would draw at brightness = 255.
+///
uint32_t calculate_unscaled_power_mW( const CRGB* ledbuffer, uint16_t numLeds);
+/// calculate_max_brightness_for_power_mW tells you the highest brightness
+/// level you can use and still stay under the specified power budget. It
+/// takes a 'target brightness' which is the brightness you'd ideally like
+/// to use. The result from this function will be no higher than the
+/// target_brightess you supply, but may be lower.
uint8_t calculate_max_brightness_for_power_mW( uint8_t target_brightness, uint32_t max_power_mW);
-
+FASTLED_NAMESPACE_END
+///@}
// POWER_MGT_H
+
#endif
diff --git a/preview_changes.txt b/preview_changes.txt
index 88a9f9bc..dbbd9466 100644
--- a/preview_changes.txt
+++ b/preview_changes.txt
@@ -1,22 +1,19 @@
-* ALL UNTESTED, USE AT YOUR OWN RISK!
-* RGB based scaling, allow for color balancing
-* (The moon) dithering support
-* Teensy 3.1 support
-* Second SPI support on teensy 3.1
-* Due support
-* P9813 (aka Cool Neon Total Control Lighting support)
-* Preliminary TM1829 support (broken, don't use!)
-* Random code changes and cleanups
-* More accurate timing adjustments for k20 and due clockless chips
-* Added HUE_RED, HUE_ORANGE, etc.
-* Added named color correction profiles (eg. TypicalSMD5050)
-* Added XY Matrix example
-* Added Fire2012 example, added HeatColor(...) to the library
-* Added sin8, cos8, quadwave8, cubicwave8, triwave8, and ease8InOutQuad
-* Added map8
-* Adjusted VIRTAL_BITS of dithering from a flickery 8 to a more solid 3
-* Added array versions of fade_video, fade_raw, and some variations
-* Added fill_gradient
-* Added inoise8/inoise16 and example program
-* Added LEDS.countFPS() for debugging framerate counts. Bit rough at the moment, thought
-* Added Palettes and associated functions and presets
+FastLED 3.1 preview changes:
+* UART in SPI mode support on AVR
+* Support for using both hardware SPI pinsets on the Teensy 3.x - 11/13 and 7/14.
+* Added UCS1904 support
+* Better AVR cycle counting
+* Split WS2812/WS2811 timings
+* Added DOTSTAR definition for adafruit dotstar pixels (aka APA102)
+* 8-way parallel output on teensy 3, 3.1 (portc,portd), due/digix (porta, portb, portd)
+* 12-way parallel output on teensy 3, 3.1 (portc)
+* 16-way parallel output on teensy 3, 3.1 (portc & portd paired)
+* refresh rate limiting
+* interrupt friendly code on teensy 3/3.1
+* -interrupt friendly code on AVR- <-- disabled for now
+* interrupt friendly code on the due
+* code re-org for future wider platform support
+* Spark Core support
+* arduino zero support (no hardware spi yet)
+* greatly improved clockless output for avr
+* greatly improved clockless output for arm m0 boards
diff --git a/release_notes.md b/release_notes.md
index 332cb73f..0390c6bf 100644
--- a/release_notes.md
+++ b/release_notes.md
@@ -97,7 +97,7 @@ FastLED2
* Enforce intra-frame timing for ws2801s
* SM16716 support
* Add #define FAST_SPI_INTERRUPTS_WRITE_PINS to make sure world is ok w/interrupts and SPI
-* Add #define FORCE_SOFTWARE_SPI for those times when you absolutely don't want to use hardware SPI, ev
+* Add #define FASTLED_FORCE_SOFTWARE_SPI for those times when you absolutely don't want to use hardware SPI, ev
en if you're using the hardware SPI pins
* Add pin definitions for the arduino megas - should fix ws2811 support
* Add pin definitions for the leonardo - should fix spi support and pin mappings
diff --git a/wiring.cpp b/wiring.cpp
new file mode 100644
index 00000000..b2af51cd
--- /dev/null
+++ b/wiring.cpp
@@ -0,0 +1,238 @@
+#define FASTLED_INTERNAL
+#include "FastLED.h"
+
+FASTLED_USING_NAMESPACE
+
+#if 0
+
+#if defined(FASTLED_AVR) && !defined(TEENSYDUINO) && !defined(LIB8_ATTINY)
+extern "C" {
+// the prescaler is set so that timer0 ticks every 64 clock cycles, and the
+// the overflow handler is called every 256 ticks.
+#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))
+
+typedef union { unsigned long _long; uint8_t raw[4]; } tBytesForLong;
+// tBytesForLong FastLED_timer0_overflow_count;
+volatile unsigned long FastLED_timer0_overflow_count=0;
+volatile unsigned long FastLED_timer0_millis = 0;
+
+LIB8STATIC void __attribute__((always_inline)) fastinc32 (volatile uint32_t & _long) {
+ uint8_t b = ++((tBytesForLong&)_long).raw[0];
+ if(!b) {
+ b = ++((tBytesForLong&)_long).raw[1];
+ if(!b) {
+ b = ++((tBytesForLong&)_long).raw[2];
+ if(!b) {
+ ++((tBytesForLong&)_long).raw[3];
+ }
+ }
+ }
+}
+
+#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
+ISR(TIM0_OVF_vect)
+#else
+ISR(TIMER0_OVF_vect)
+#endif
+{
+ fastinc32(FastLED_timer0_overflow_count);
+ // FastLED_timer0_overflow_count++;
+}
+
+// there are 1024 microseconds per overflow counter tick.
+unsigned long millis()
+{
+ unsigned long m;
+ uint8_t oldSREG = SREG;
+
+ // disable interrupts while we read FastLED_timer0_millis or we might get an
+ // inconsistent value (e.g. in the middle of a write to FastLED_timer0_millis)
+ cli();
+ m = FastLED_timer0_overflow_count; //._long;
+ SREG = oldSREG;
+
+ return (m*(MICROSECONDS_PER_TIMER0_OVERFLOW/8))/(1000/8);
+}
+
+unsigned long micros() {
+ unsigned long m;
+ uint8_t oldSREG = SREG, t;
+
+ cli();
+ m = FastLED_timer0_overflow_count; // ._long;
+#if defined(TCNT0)
+ t = TCNT0;
+#elif defined(TCNT0L)
+ t = TCNT0L;
+#else
+ #error TIMER 0 not defined
+#endif
+
+
+#ifdef TIFR0
+ if ((TIFR0 & _BV(TOV0)) && (t < 255))
+ m++;
+#else
+ if ((TIFR & _BV(TOV0)) && (t < 255))
+ m++;
+#endif
+
+ SREG = oldSREG;
+
+ return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
+}
+
+void delay(unsigned long ms)
+{
+ uint16_t start = (uint16_t)micros();
+
+ while (ms > 0) {
+ if (((uint16_t)micros() - start) >= 1000) {
+ ms--;
+ start += 1000;
+ }
+ }
+}
+
+#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
+void init()
+{
+ // this needs to be called before setup() or some functions won't
+ // work there
+ sei();
+
+ // on the ATmega168, timer 0 is also used for fast hardware pwm
+ // (using phase-correct PWM would mean that timer 0 overflowed half as often
+ // resulting in different millis() behavior on the ATmega8 and ATmega168)
+#if defined(TCCR0A) && defined(WGM01)
+ sbi(TCCR0A, WGM01);
+ sbi(TCCR0A, WGM00);
+#endif
+
+ // set timer 0 prescale factor to 64
+#if defined(__AVR_ATmega128__)
+ // CPU specific: different values for the ATmega128
+ sbi(TCCR0, CS02);
+#elif defined(TCCR0) && defined(CS01) && defined(CS00)
+ // this combination is for the standard atmega8
+ sbi(TCCR0, CS01);
+ sbi(TCCR0, CS00);
+#elif defined(TCCR0B) && defined(CS01) && defined(CS00)
+ // this combination is for the standard 168/328/1280/2560
+ sbi(TCCR0B, CS01);
+ sbi(TCCR0B, CS00);
+#elif defined(TCCR0A) && defined(CS01) && defined(CS00)
+ // this combination is for the __AVR_ATmega645__ series
+ sbi(TCCR0A, CS01);
+ sbi(TCCR0A, CS00);
+#else
+ #error Timer 0 prescale factor 64 not set correctly
+#endif
+
+ // enable timer 0 overflow interrupt
+#if defined(TIMSK) && defined(TOIE0)
+ sbi(TIMSK, TOIE0);
+#elif defined(TIMSK0) && defined(TOIE0)
+ sbi(TIMSK0, TOIE0);
+#else
+ #error Timer 0 overflow interrupt not set correctly
+#endif
+
+ // timers 1 and 2 are used for phase-correct hardware pwm
+ // this is better for motors as it ensures an even waveform
+ // note, however, that fast pwm mode can achieve a frequency of up
+ // 8 MHz (with a 16 MHz clock) at 50% duty cycle
+
+#if defined(TCCR1B) && defined(CS11) && defined(CS10)
+ TCCR1B = 0;
+
+ // set timer 1 prescale factor to 64
+ sbi(TCCR1B, CS11);
+#if F_CPU >= 8000000L
+ sbi(TCCR1B, CS10);
+#endif
+#elif defined(TCCR1) && defined(CS11) && defined(CS10)
+ sbi(TCCR1, CS11);
+#if F_CPU >= 8000000L
+ sbi(TCCR1, CS10);
+#endif
+#endif
+ // put timer 1 in 8-bit phase correct pwm mode
+#if defined(TCCR1A) && defined(WGM10)
+ sbi(TCCR1A, WGM10);
+#elif defined(TCCR1)
+ #warning this needs to be finished
+#endif
+
+ // set timer 2 prescale factor to 64
+#if defined(TCCR2) && defined(CS22)
+ sbi(TCCR2, CS22);
+#elif defined(TCCR2B) && defined(CS22)
+ sbi(TCCR2B, CS22);
+#else
+ #warning Timer 2 not finished (may not be present on this CPU)
+#endif
+
+ // configure timer 2 for phase correct pwm (8-bit)
+#if defined(TCCR2) && defined(WGM20)
+ sbi(TCCR2, WGM20);
+#elif defined(TCCR2A) && defined(WGM20)
+ sbi(TCCR2A, WGM20);
+#else
+ #warning Timer 2 not finished (may not be present on this CPU)
+#endif
+
+#if defined(TCCR3B) && defined(CS31) && defined(WGM30)
+ sbi(TCCR3B, CS31); // set timer 3 prescale factor to 64
+ sbi(TCCR3B, CS30);
+ sbi(TCCR3A, WGM30); // put timer 3 in 8-bit phase correct pwm mode
+#endif
+
+#if defined(TCCR4A) && defined(TCCR4B) && defined(TCCR4D) /* beginning of timer4 block for 32U4 and similar */
+ sbi(TCCR4B, CS42); // set timer4 prescale factor to 64
+ sbi(TCCR4B, CS41);
+ sbi(TCCR4B, CS40);
+ sbi(TCCR4D, WGM40); // put timer 4 in phase- and frequency-correct PWM mode
+ sbi(TCCR4A, PWM4A); // enable PWM mode for comparator OCR4A
+ sbi(TCCR4C, PWM4D); // enable PWM mode for comparator OCR4D
+#else /* beginning of timer4 block for ATMEGA1280 and ATMEGA2560 */
+#if defined(TCCR4B) && defined(CS41) && defined(WGM40)
+ sbi(TCCR4B, CS41); // set timer 4 prescale factor to 64
+ sbi(TCCR4B, CS40);
+ sbi(TCCR4A, WGM40); // put timer 4 in 8-bit phase correct pwm mode
+#endif
+#endif /* end timer4 block for ATMEGA1280/2560 and similar */
+
+#if defined(TCCR5B) && defined(CS51) && defined(WGM50)
+ sbi(TCCR5B, CS51); // set timer 5 prescale factor to 64
+ sbi(TCCR5B, CS50);
+ sbi(TCCR5A, WGM50); // put timer 5 in 8-bit phase correct pwm mode
+#endif
+
+#if defined(ADCSRA)
+ // set a2d prescale factor to 128
+ // 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range.
+ // XXX: this will not work properly for other clock speeds, and
+ // this code should use F_CPU to determine the prescale factor.
+ sbi(ADCSRA, ADPS2);
+ sbi(ADCSRA, ADPS1);
+ sbi(ADCSRA, ADPS0);
+
+ // enable a2d conversions
+ sbi(ADCSRA, ADEN);
+#endif
+
+ // the bootloader connects pins 0 and 1 to the USART; disconnect them
+ // here so they can be used as normal digital i/o; they will be
+ // reconnected in Serial.begin()
+#if defined(UCSRB)
+ UCSRB = 0;
+#elif defined(UCSR0B)
+ UCSR0B = 0;
+#endif
+}
+};
+#endif
+
+#endif
+