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:
-rw-r--r--FastLED.cpp161
-rw-r--r--FastLED.h239
-rw-r--r--README.md19
-rw-r--r--block_clockless.h232
-rw-r--r--chipsets.h371
-rw-r--r--clockless.h277
-rw-r--r--clockless2.h241
-rw-r--r--clockless_arm_k20.h144
-rw-r--r--clockless_arm_sam.h217
-rw-r--r--clockless_trinket.h408
-rw-r--r--color.h54
-rw-r--r--colorpalettes.cpp164
-rw-r--r--colorpalettes.h42
-rw-r--r--colorutils.cpp579
-rw-r--r--colorutils.h804
-rw-r--r--controller.h324
-rw-r--r--delay.h77
-rw-r--r--dmx.h85
-rw-r--r--examples/AnalogOutput/AnalogOutput.ino65
-rw-r--r--examples/Blink/Blink.ino9
-rw-r--r--examples/ColorPalette/ColorPalette.ino189
-rw-r--r--examples/ColorTemperature/ColorTemperature.ino85
-rw-r--r--examples/Cylon/Cylon.ino8
-rw-r--r--examples/Fast2Dev/Fast2Dev.ino99
-rw-r--r--examples/Fire2012/Fire2012.ino96
-rw-r--r--examples/Fire2012WithPalette/Fire2012WithPalette.ino155
-rw-r--r--examples/FirstLight/FirstLight.ino17
-rw-r--r--examples/Multiple/ArrayOfLedArrays/ArrayOfLedArrays.ino37
-rw-r--r--examples/Multiple/MirroringSample/MirroringSample.ino44
-rw-r--r--examples/Multiple/MultiArrays/MultiArrays.ino52
-rw-r--r--examples/Multiple/MultipleStripsInOneArray/MultipleStripsInOneArray.ino34
-rw-r--r--examples/Noise/Noise.ino112
-rw-r--r--examples/NoisePlayground/NoisePlayground.ino73
-rw-r--r--examples/NoisePlusPalette/NoisePlusPalette.ino273
-rw-r--r--examples/Pintest/Pintest.ino105
-rw-r--r--examples/RGBCalibrate/RGBCalibrate.ino27
-rw-r--r--examples/SmartMatrix/SmartMatrix.ino121
-rw-r--r--examples/XYMatrix/XYMatrix.ino197
-rw-r--r--fastpin.h333
-rw-r--r--fastpin_arm_k20.h105
-rw-r--r--fastpin_arm_sam.h107
-rw-r--r--fastpin_avr.h199
-rw-r--r--fastspi.h33
-rw-r--r--fastspi_arm_k20.h (renamed from fastspi_arm.h)287
-rw-r--r--fastspi_arm_sam.h160
-rw-r--r--fastspi_avr.h168
-rw-r--r--fastspi_bitbang.h166
-rw-r--r--fastspi_nop.h63
-rw-r--r--fastspi_ref.h98
-rw-r--r--hsv2rgb.cpp31
-rw-r--r--hsv2rgb.h11
-rw-r--r--keywords.txt123
-rw-r--r--led_sysdefs.h52
-rw-r--r--lib8tion.h776
-rw-r--r--noise.cpp696
-rw-r--r--noise.h116
-rw-r--r--pixeltypes.h133
-rw-r--r--power_mgt.cpp176
-rw-r--r--power_mgt.h48
-rw-r--r--preview_changes.txt22
-rw-r--r--release_notes.md (renamed from release_notes.txt)63
-rw-r--r--smartmatrix_t3.h79
62 files changed, 8582 insertions, 1699 deletions
diff --git a/FastLED.cpp b/FastLED.cpp
index c9e2992c..c87e8e31 100644
--- a/FastLED.cpp
+++ b/FastLED.cpp
@@ -1,79 +1,144 @@
#include "FastLED.h"
+#if defined(__SAM3X8E__)
+volatile uint32_t fuckit;
+#endif
+
+void *pSmartMatrix = NULL;
CFastLED LEDS;
CFastLED & FastSPI_LED = LEDS;
CFastLED & FastSPI_LED2 = LEDS;
CFastLED & FastLED = LEDS;
-uint32_t CRGB::Squant = ((uint32_t)((__TIME__[4]-'0') * 28))<<16 | ((__TIME__[6]-'0')*50)<<8 | ((__TIME__[7]-'0')*28);
+CLEDController *CLEDController::m_pHead = NULL;
+CLEDController *CLEDController::m_pTail = NULL;
+
+// uint32_t CRGB::Squant = ((uint32_t)((__TIME__[4]-'0') * 28))<<16 | ((__TIME__[6]-'0')*50)<<8 | ((__TIME__[7]-'0')*28);
-CFastLED::CFastLED() {
+CFastLED::CFastLED() {
// clear out the array of led controllers
- m_nControllers = NUM_CONTROLLERS;
- m_nScale = 255;
- memset8(m_Controllers, 0, m_nControllers * sizeof(CControllerInfo));
+ // m_nControllers = 0;
+ m_Scale = 255;
+ m_nFPS = 0;
}
-CLEDController *CFastLED::addLeds(CLEDController *pLed,
- const struct CRGB *data,
- int nLedsOrOffset, int nLedsIfOffset) {
+CLEDController &CFastLED::addLeds(CLEDController *pLed,
+ struct CRGB *data,
+ int nLedsOrOffset, int nLedsIfOffset) {
int nOffset = (nLedsIfOffset > 0) ? nLedsOrOffset : 0;
int nLeds = (nLedsIfOffset > 0) ? nLedsIfOffset : nLedsOrOffset;
- int target = -1;
+ pLed->init();
+ pLed->setLeds(data + nOffset, nLeds);
+ return *pLed;
+}
- // Figure out where to put the new led controller
- for(int i = 0; i < m_nControllers; i++) {
- if(m_Controllers[i].pLedController == NULL) {
- target = i;
- break;
- }
+void CFastLED::show(uint8_t scale) {
+ CLEDController *pCur = CLEDController::head();
+ while(pCur) {
+ uint8_t d = pCur->getDither();
+ if(m_nFPS < 100) { pCur->setDither(0); }
+ pCur->showLeds(scale);
+ pCur->setDither(d);
+ pCur = pCur->next();
}
+ countFPS();
+}
- // if we have a spot, use it!
- if(target != -1) {
- m_Controllers[target].pLedController = pLed;
- m_Controllers[target].pLedData = data;
- m_Controllers[target].nOffset = nOffset;
- m_Controllers[target].nLeds = nLeds;
- pLed->init();
- return pLed;
+int CFastLED::count() {
+ int x = 0;
+ CLEDController *pCur = CLEDController::head();
+ while( pCur) {
+ x++;
+ pCur = pCur->next();
}
-
- return NULL;
+ return x;
}
-void CFastLED::show(uint8_t scale) {
- for(int i = 0; i < m_nControllers; i++) {
- if(m_Controllers[i].pLedController != NULL) {
- m_Controllers[i].pLedController->show(m_Controllers[i].pLedData + m_Controllers[i].nOffset,
- m_Controllers[i].nLeds, scale);
- } else {
- return;
- }
+CLEDController & CFastLED::operator[](int x) {
+ CLEDController *pCur = CLEDController::head();
+ while(x-- && pCur) {
+ pCur = pCur->next();
+ }
+ if(pCur == NULL) {
+ return *(CLEDController::head());
+ } else {
+ return *pCur;
}
}
-void CFastLED::showColor(const struct CRGB & color, uint8_t scale) {
- for(int i = 0; i < m_nControllers; i++) {
- if(m_Controllers[i].pLedController != NULL) {
- m_Controllers[i].pLedController->showColor(color, m_Controllers[i].nLeds, scale);
- } else {
- return;
- }
+void CFastLED::showColor(const struct CRGB & color, uint8_t scale) {
+ CLEDController *pCur = CLEDController::head();
+ while(pCur) {
+ uint8_t d = pCur->getDither();
+ if(m_nFPS < 100) { pCur->setDither(0); }
+ pCur->showColor(color, scale);
+ pCur->setDither(d);
+ pCur = pCur->next();
}
+ countFPS();
}
-void CFastLED::clear(boolean writeData) {
- if(writeData) {
+void CFastLED::clear(boolean writeData) {
+ if(writeData) {
showColor(CRGB(0,0,0), 0);
}
- for(int i = 0; i < m_nControllers; i++) {
- if(m_Controllers[i].pLedData != NULL) {
- memset8((void*)m_Controllers[i].pLedData, 0, sizeof(struct CRGB) * m_Controllers[i].nLeds);
- } else {
- return;
- }
+ clearData();
+}
+
+void CFastLED::clearData() {
+ CLEDController *pCur = CLEDController::head();
+ while(pCur) {
+ pCur->clearLedData();
+ pCur = pCur->next();
+ }
+}
+
+void CFastLED::delay(unsigned long ms) {
+ unsigned long start = millis();
+ while((millis()-start) < ms) {
+ ::delay(1);
+ show();
}
}
+
+void CFastLED::setTemperature(const struct CRGB & temp) {
+ CLEDController *pCur = CLEDController::head();
+ while(pCur) {
+ pCur->setTemperature(temp);
+ pCur = pCur->next();
+ }
+}
+
+void CFastLED::setCorrection(const struct CRGB & correction) {
+ CLEDController *pCur = CLEDController::head();
+ while(pCur) {
+ pCur->setCorrection(correction);
+ pCur = pCur->next();
+ }
+}
+
+void CFastLED::setDither(uint8_t ditherMode) {
+ CLEDController *pCur = CLEDController::head();
+ while(pCur) {
+ pCur->setDither(ditherMode);
+ pCur = pCur->next();
+ }
+}
+
+extern int noise_min;
+extern int noise_max;
+
+void CFastLED::countFPS(int nFrames) {
+ static int br = 0;
+ static uint32_t lastframe = 0; // millis();
+
+ if(br++ >= nFrames) {
+ uint32_t now = millis();
+ now -= lastframe;
+ m_nFPS = (br * 1000) / now;
+ br = 0;
+ lastframe = millis();
+ }
+}
diff --git a/FastLED.h b/FastLED.h
index 43bbf181..19b1b8b7 100644
--- a/FastLED.h
+++ b/FastLED.h
@@ -1,142 +1,239 @@
#ifndef __INC_FASTSPI_LED2_H
#define __INC_FASTSPI_LED2_H
+// #define NO_CORRECTION 1
+// #define NO_DITHERING 1
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+#define FASTLED_VERSION 2001001
+#warning FastLED version 2001001 (Not really a warning, just telling you here.)
+
+#define __PROG_TYPES_COMPAT__
+
+#ifdef SmartMatrix_h
+#include<SmartMatrix.h>
+#endif
+
+#ifdef DmxSimple_h
+#include<DmxSimple.h>
+#endif
+
+#ifdef DmxSerial_h
+#include<DMXSerial.h>
+#endif
+
#include "controller.h"
#include "fastpin.h"
#include "fastspi.h"
#include "clockless.h"
-#include "clockless_trinket.h"
#include "lib8tion.h"
#include "hsv2rgb.h"
+#include "colorutils.h"
+#include "colorpalettes.h"
#include "chipsets.h"
-#include "dmx.h"
+#include "./dmx.h"
+#include "smartmatrix_t3.h"
+#include "noise.h"
+#include "power_mgt.h"
enum ESPIChipsets {
LPD8806,
WS2801,
- SM16716
+ WS2803,
+ SM16716,
+ P9813,
+ APA102
};
-enum EClocklessChipsets {
- DMX,
- TM1809,
- TM1804,
- TM1803,
- WS2811,
- WS2812,
- WS2812B,
- WS2811_400,
- NEOPIXEL,
- UCS1903
+enum ESM { SMART_MATRIX };
+
+template<uint8_t DATA_PIN> class NEOPIXEL : public WS2811Controller800Khz<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 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> {};
+template<uint8_t DATA_PIN, EOrder RGB_ORDER> class GW6205 : public GW6205Controller800Khz<DATA_PIN, RGB_ORDER> {};
+template<uint8_t DATA_PIN, EOrder RGB_ORDER> class GW6205_400 : public GW6205Controller400Khz<DATA_PIN, RGB_ORDER> {};
+template<uint8_t DATA_PIN, EOrder RGB_ORDER> class LPD1886 : public LPD1886Controller1250Khz<DATA_PIN, RGB_ORDER> {};
+#ifdef DmxSimple_h
+template<uint8_t DATA_PIN, EOrder RGB_ORDER> class DMXSIMPLE : public DMXSimpleController<DATA_PIN, RGB_ORDER> {};
+#endif
+#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> {};
+
+enum EBlockChipsets {
+ WS2811_PORTC
};
+#if defined(LIB8_ATTINY)
+#define NUM_CONTROLLERS 2
+#else
#define NUM_CONTROLLERS 8
+#endif
class CFastLED {
- struct CControllerInfo {
- CLEDController *pLedController;
- const struct CRGB *pLedData;
- int nLeds;
- int nOffset;
- };
-
- CControllerInfo m_Controllers[NUM_CONTROLLERS];
- int m_nControllers;
- uint8_t m_nScale;
-
+ // int m_nControllers;
+ uint8_t m_Scale;
+ uint16_t m_nFPS;
public:
CFastLED();
- CLEDController *addLeds(CLEDController *pLed, const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0);
+ static CLEDController &addLeds(CLEDController *pLed, struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0);
- template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN > CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
- switch(CHIPSET) {
+ 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); }
case WS2801: { static WS2801Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+ case WS2803: { static WS2803Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SM16716: { static SM16716Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+ case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+ case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
}
}
- template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER > CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
- switch(CHIPSET) {
+ template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER > static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+ switch(CHIPSET) {
case LPD8806: { static LPD8806Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case WS2801: { static WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+ case WS2803: { static WS2803Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SM16716: { static SM16716Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+ case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
+ case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
}
}
-
- template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER, uint8_t SPI_DATA_RATE > CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
- switch(CHIPSET) {
+
+ 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 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> CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+ 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);
- }
+ }
- template<ESPIChipsets CHIPSET, EOrder RGB_ORDER> CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+ template<ESPIChipsets CHIPSET, EOrder RGB_ORDER> static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
return addLeds<CHIPSET, SPI_DATA, SPI_CLOCK, RGB_ORDER>(data, nLedsOrOffset, nLedsIfOffset);
- }
+ }
- template<ESPIChipsets CHIPSET, EOrder RGB_ORDER, uint8_t SPI_DATA_RATE> CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+ template<ESPIChipsets CHIPSET, EOrder RGB_ORDER, uint8_t SPI_DATA_RATE> static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
return addLeds<CHIPSET, SPI_DATA, SPI_CLOCK, RGB_ORDER, SPI_DATA_RATE>(data, nLedsOrOffset, nLedsIfOffset);
- }
+ }
#endif
- template<EClocklessChipsets CHIPSET, uint8_t DATA_PIN>
- CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
- switch(CHIPSET) {
-#ifdef FASTSPI_USE_DMX_SIMPLE
- case DMX: { static DMXController<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
-#endif
- case TM1809: { static TM1809Controller800Khz<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
- case TM1803: { static TM1803Controller400Khz<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
- case UCS1903: { static UCS1903Controller400Khz<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
- case WS2812:
- case WS2812B:
- case WS2811: { static WS2811Controller800Khz<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
- case NEOPIXEL: { static WS2811Controller800Khz<DATA_PIN, GRB> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
- case WS2811_400: { static WS2811Controller400Khz<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
- }
+ 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);
+ }
+
+ 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;
+ return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
}
- template<EClocklessChipsets CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER>
- CLEDController *addLeds(const struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
- switch(CHIPSET) {
-#ifdef FASTSPI_USE_DMX_SIMPLE
- case DMX: {static DMXController<DATA_PIN, RGB_ORDER> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+ template<template<uint8_t DATA_PIN, EOrder RGB_ORDER> class CHIPSET, uint8_t DATA_PIN>
+ static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
+ static CHIPSET<DATA_PIN, RGB> 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);
+ }
+
+ 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);
+ }
+
+#ifdef SmartMatrix_h
+ template<ESM CHIPSET>
+ static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
+ {
+ switch(CHIPSET) {
+ case SMART_MATRIX: { static CSmartMatrixController controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+ }
+ }
#endif
- case TM1809: { static TM1809Controller800Khz<DATA_PIN, RGB_ORDER> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
- case TM1803: { static TM1803Controller400Khz<DATA_PIN, RGB_ORDER> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
- case UCS1903: { static UCS1903Controller400Khz<DATA_PIN, RGB_ORDER> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
- case WS2812:
- case WS2812B:
- case NEOPIXEL:
- case WS2811: { static WS2811Controller800Khz<DATA_PIN, RGB_ORDER> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
- case WS2811_400: { static WS2811Controller400Khz<DATA_PIN, RGB_ORDER> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
+
+#ifdef HAS_BLOCKLESS
+ template<EBlockChipsets CHIPSET, int NUM_LANES>
+ 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);
}
}
+#endif
- void setBrightness(uint8_t scale) { m_nScale = scale; }
- uint8_t getBrightness() { return m_nScale; }
+ void setBrightness(uint8_t scale) { m_Scale = scale; }
+ uint8_t getBrightness() { return m_Scale; }
/// Update all our controllers with the current led colors, using the passed in brightness
void show(uint8_t scale);
/// Update all our controllers with the current led colors
- void show() { show(m_nScale); }
+ void show() { show(m_Scale); }
void clear(boolean writeData = false);
+ void clearData();
+
void showColor(const struct CRGB & color, uint8_t scale);
- void showColor(const struct CRGB & color) { showColor(color, m_nScale); }
+ void showColor(const struct CRGB & color) { showColor(color, m_Scale); }
+
+ void delay(unsigned long ms);
+
+ void setTemperature(const struct CRGB & temp);
+ void setCorrection(const struct CRGB & correction);
+ 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.
+ void countFPS(int nFrames=25);
+ // Get the number of frames/second being written out
+ uint16_t getFPS() { return m_nFPS; }
+
+ // returns the number of controllers (strips) that have been added with addLeds
+ int count();
+
+ // returns the Nth controller
+ CLEDController & operator[](int x);
+
+ // Convenience functions for single-strip setups:
+
+ // returns the number of LEDs in the first strip
+ int size() { return (*this)[0].size(); }
+ // returns pointer to the CRGB buffer for the first strip
+ CRGB *leds() { return (*this)[0].leds(); }
};
extern CFastLED & FastSPI_LED;
diff --git a/README.md b/README.md
index cc225747..b0ac3f22 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,9 @@ IMPORTANT NOTE: If you are building for AVR based systems, please do not use any
FastLED
=======
+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
leds, this library also includes a number of functions for high-performing 8bit math for manipulating
@@ -20,10 +23,7 @@ We have multiple goals with this library:
## Getting help
-If you need help with using the library, please consider going to the google+ community first, which
- is at http://fastled.io/+ - there are hundreds of people in that group and many times you will get
-a quicker answer to your question there, as you will be likely to run into other people who have had
- the same issue. If you run into bugs with the library (compilation failures, the library doing the wrong thing), or if you'd like to request that we support a particular platform or LED chipset, then please open an issue at http://fastled.io/issues and we will try to figure out what is going wrong.
+If you need help with using the library, please consider going to the google+ community first, which is at http://fastled.io/+ - there are hundreds of people in that group and many times you will get a quicker answer to your question there, as you will be likely to run into other people who have had the same issue. If you run into bugs with the library (compilation failures, the library doing the wrong thing), or if you'd like to request that we support a particular platform or LED chipset, then please open an issue at http://fastled.io/issues and we will try to figure out what is going wrong.
## Simple example
@@ -46,10 +46,15 @@ Here's a list of all the LED chipsets are supported. More details on the led ch
* 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
+* GW6205 - another 3 wire led chipset
* LPD8806 - SPI based chpiset, very high speed
* WS2801 - SPI based chipset, cheap and widely available
* SM16716 - SPI based chipset
+* APA102 - SPI based chipset
+* P9813 - aka Cool Neon's Total Control Lighting
* DMX - send rgb data out over DMX using arduino DMX libraries
+* SmartMatrix panels - needs the SmartMatrix library - https://github.com/pixelmatix/SmartMatrix
+
LPD6803, HL1606, and "595"-style shift registers are no longer supported by the library. The older Version 1 of the library ("FastSPI_LED") has support for these, but is missing many of the advanced features of current versions and is no longer being maintained.
@@ -59,9 +64,11 @@ LPD6803, HL1606, and "595"-style shift registers are no longer supported by the
Right now the library is supported on a variety of arduino compatable platforms. If it's ARM or AVR and uses the arduino software (or a modified version of it to build) then it is likely supported. Note that we have a long list of upcoming platforms to support, so if you don't see what you're looking for here, ask, it may be on the roadmap (or may already be supported). N.B. at the moment we are only supporting the stock compilers that ship with the arduino software. Support for upgraded compilers, as well as using AVR studio and skipping the arduino entirely, should be coming in a near future release.
* Arduino & compatibles - straight up arduino devices, uno, duo, leonardo, mega, nano, etc...
-* Teensy 2, Teensy++ 2, Teensy 3 - arduino compataible from pjrc.com with some extra goodies (note the teensy 3 is ARM, not AVR!)
+* 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!)
+* Arduino Due and the digistump DigiX
-What types of platforms are we thinking about supporting in the future? Here's a short list: Arduino Due, MSP430, ChipKit32, Maple, Beagleboard
+What types of platforms are we thinking about supporting in the future? Here's a short list: RFDuino, SparkCore, MSP430, ChipKit32, Maple, Beagleboard
## What about that name?
diff --git a/block_clockless.h b/block_clockless.h
new file mode 100644
index 00000000..d561239c
--- /dev/null
+++ b/block_clockless.h
@@ -0,0 +1,232 @@
+ #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 7c8d4e1d..5e15baa6 100644
--- a/chipsets.h
+++ b/chipsets.h
@@ -2,6 +2,7 @@
#define __INC_CHIPSETS_H
#include "pixeltypes.h"
+#include "clockless.h"
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
@@ -9,17 +10,16 @@
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(24) >
+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;
class LPD8806_ADJUST {
public:
// LPD8806 spec wants the high bit of every rgb data byte sent out to be set.
- __attribute__((always_inline)) inline static uint8_t adjust(register uint8_t data) { return (data>>1) | 0x80; }
- __attribute__((always_inline)) inline static uint8_t adjust(register uint8_t data, register uint8_t scale) { return (scale8(data, scale)>>1) | 0x80; }
- __attribute__((always_inline)) inline static void postBlock(int len) {
- SPI::writeBytesValueRaw(0, ((len+63)>>6));
+ __attribute__((always_inline)) inline static uint8_t adjust(register uint8_t data) { return ((data>>1) | 0x80) + ((data && (data<254)) & 0x01); }
+ __attribute__((always_inline)) inline static void postBlock(int len) {
+ SPI::writeBytesValueRaw(0, ((len*3+63)>>6));
}
};
@@ -27,15 +27,15 @@ class LPD8806Controller : public CLEDController {
SPI mSPI;
int mClearedLeds;
- void checkClear(int nLeds) {
- if(nLeds > mClearedLeds) {
+ void checkClear(int nLeds) {
+ if(nLeds > mClearedLeds) {
clearLine(nLeds);
mClearedLeds = nLeds;
}
}
-
- void clearLine(int nLeds) {
- int n = ((nLeds + 63) >> 6);
+
+ void clearLine(int nLeds) {
+ int n = ((nLeds*3 + 63) >> 6);
mSPI.writeBytesValue(0, n);
}
public:
@@ -45,39 +45,28 @@ public:
mClearedLeds = 0;
}
- virtual void clearLeds(int nLeds) {
+ virtual void clearLeds(int nLeds) {
mSPI.select();
- mSPI.writeBytesValueRaw(0x80, nLeds * 3);
+ mSPI.writeBytesValueRaw(0x80, nLeds * 3);
mSPI.writeBytesValueRaw(0, ((nLeds*3+63)>>6));
mSPI.release();
}
- virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
- mSPI.select();
- uint8_t a = 0x80 | (scale8(data[RGB_BYTE0(RGB_ORDER)], scale) >> 1);
- uint8_t b = 0x80 | (scale8(data[RGB_BYTE1(RGB_ORDER)], scale) >> 1);
- uint8_t c = 0x80 | (scale8(data[RGB_BYTE2(RGB_ORDER)], scale) >> 1);
- int iLeds = 0;
-
- while(iLeds++ < nLeds) {
- mSPI.writeByte(a);
- mSPI.writeByte(b);
- mSPI.writeByte(c);
- }
+protected:
- // latch in the world
- mSPI.writeBytesValueRaw(0, ((nLeds*3+63)>>6));
- mSPI.release();
+ virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) {
+ mSPI.template writePixels<0, LPD8806_ADJUST, RGB_ORDER>(PixelController<RGB_ORDER>(data, nLeds, scale, getDither()));
}
- virtual void show(const struct CRGB *data, int nLeds, uint8_t scale = 255) {
- mSPI.template writeBytes3<LPD8806_ADJUST, RGB_ORDER>((byte*)data, nLeds * 3, scale);
+ virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
+ // TODO rgb-ize scale
+ mSPI.template writePixels<0, LPD8806_ADJUST, RGB_ORDER>(PixelController<RGB_ORDER>(data, nLeds, scale, getDither()));
}
#ifdef SUPPORT_ARGB
virtual void show(const struct CARGB *data, int nLeds, uint8_t scale) {
checkClear(nLeds);
- mSPI.template writeBytes3<1, LPD8806_ADJUST, RGB_ORDER>((byte*)data, nLeds * 4, scale);
+ mSPI.template writePixels<0, LPD8806_ADJUST, RGB_ORDER>(PixelController<RGB_ORDER>(data, nLeds, scale, getDither()));
}
#endif
};
@@ -97,49 +86,209 @@ class WS2801Controller : public CLEDController {
public:
WS2801Controller() {}
- virtual void init() {
+ virtual void init() {
mSPI.init();
mWaitDelay.mark();
}
- virtual void clearLeds(int nLeds) {
+ virtual void clearLeds(int nLeds) {
mWaitDelay.wait();
mSPI.writeBytesValue(0, nLeds*3);
mWaitDelay.mark();
}
-
- virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
+
+protected:
+
+ virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) {
mWaitDelay.wait();
- mSPI.select();
- uint8_t a = scale8(data[RGB_BYTE0(RGB_ORDER)], scale);
- uint8_t b = scale8(data[RGB_BYTE1(RGB_ORDER)], scale);
- uint8_t c = scale8(data[RGB_BYTE2(RGB_ORDER)], scale);
-
- while(nLeds--) {
- mSPI.writeByte(a);
- mSPI.writeByte(b);
- mSPI.writeByte(c);
- }
- mSPI.waitFully();
- mSPI.release();
+ mSPI.template writePixels<0, DATA_NOP, RGB_ORDER>(PixelController<RGB_ORDER>(data, nLeds, scale, getDither()));
mWaitDelay.mark();
}
- virtual void show(const struct CRGB *data, int nLeds, uint8_t scale) {
+ virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
mWaitDelay.wait();
- mSPI.template writeBytes3<0, RGB_ORDER>((byte*)data, nLeds * 3, scale);
+ mSPI.template writePixels<0, DATA_NOP, RGB_ORDER>(PixelController<RGB_ORDER>(data, nLeds, scale, getDither()));
mWaitDelay.mark();
}
#ifdef SUPPORT_ARGB
- virtual void show(const struct CRGB *data, int nLeds, uint8_t scale) {
+ virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
mWaitDelay.wait();
- mSPI.template writeBytes3<1, RGB_ORDER>((byte*)data, nLeds * 4, scale);
+ mSPI.template writePixels<0, DATA_NOP, RGB_ORDER>(PixelController<RGB_ORDER>(data, nLeds, scale, getDither()));
mWaitDelay.mark();
}
#endif
};
+template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(25)>
+class WS2803Controller : public WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED> {};
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// APA102 definition - takes data/clock/select pin values (N.B. should take an SPI definition?)
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = BGR, uint8_t SPI_SPEED = DATA_RATE_MHZ(12)>
+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); */}
+
+ 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);
+ }
+
+public:
+ APA102Controller() {}
+
+ virtual void init() {
+ mSPI.init();
+ }
+
+ virtual void clearLeds(int nLeds) {
+ showColor(CRGB(0,0,0), nLeds, CRGB(0,0,0));
+ }
+
+protected:
+
+ virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(data, nLeds, scale, getDither());
+
+ mSPI.select();
+
+ startBoundary();
+ while(nLeds--) {
+ writeLed(pixels.loadAndScale0(), pixels.loadAndScale1(), pixels.loadAndScale2());
+ pixels.stepDithering();
+ }
+ endBoundary();
+
+ mSPI.waitFully();
+ mSPI.release();
+ }
+
+ virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(data, nLeds, scale, getDither());
+
+ mSPI.select();
+
+ startBoundary();
+ for(int i = 0; i < nLeds; i++) {
+ writeLed(pixels.loadAndScale0(), pixels.loadAndScale1(), pixels.loadAndScale2());
+ pixels.advanceData();
+ pixels.stepDithering();
+ }
+ endBoundary();
+
+ mSPI.release();
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(data, nLeds,, scale, getDither());
+
+ mSPI.select();
+
+ startBoundary();
+ for(int i = 0; i < nLeds; i++) {
+ writeLed(pixels.loadAndScale0(), pixels.loadAndScale1(), pixels.loadAndScale2());
+ pixels.advanceData();
+ pixels.stepDithering();
+ }
+ endBoundary();
+
+ mSPI.release();
+ }
+#endif
+};
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// P9813 definition - takes data/clock/select pin values (N.B. should take an SPI definition?)
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+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;
+ SPI mSPI;
+
+ void writeBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); }
+
+ inline void writeLed(uint8_t r, uint8_t g, uint8_t b) __attribute__((always_inline)) {
+ register uint8_t top = 0xC0 | ((~b & 0xC0) >> 2) | ((~g & 0xC0) >> 4) | ((~r & 0xC0) >> 6);
+ mSPI.writeByte(top); mSPI.writeByte(b); mSPI.writeByte(g); mSPI.writeByte(r);
+ }
+
+public:
+ P9813Controller() {}
+
+ virtual void init() {
+ mSPI.init();
+ }
+
+ virtual void clearLeds(int nLeds) {
+ showColor(CRGB(0,0,0), nLeds, CRGB(0,0,0));
+ }
+
+protected:
+
+ virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(data, nLeds, scale, getDither());
+
+ mSPI.select();
+
+ writeBoundary();
+ while(nLeds--) {
+ writeLed(pixels.loadAndScale0(), pixels.loadAndScale1(), pixels.loadAndScale2());
+ pixels.stepDithering();
+ }
+ writeBoundary();
+
+ mSPI.waitFully();
+ mSPI.release();
+ }
+
+ virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(data, nLeds, scale, getDither());
+
+ mSPI.select();
+
+ writeBoundary();
+ for(int i = 0; i < nLeds; i++) {
+ writeLed(pixels.loadAndScale0(), pixels.loadAndScale1(), pixels.loadAndScale2());
+ pixels.advanceData();
+ pixels.stepDithering();
+ }
+ writeBoundary();
+
+ mSPI.release();
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
+ PixelController<RGB_ORDER> pixels(data, nLeds,, scale, getDither());
+
+ mSPI.select();
+
+ writeBoundary();
+ for(int i = 0; i < nLeds; i++) {
+ writeLed(pixels.loadAndScale0(), pixels.loadAndScale1(), pixels.loadAndScale2());
+ pixels.advanceData();
+ pixels.stepDithering();
+ }
+ writeBoundary();
+
+ mSPI.release();
+ }
+#endif
+};
+
+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SM16716 definition - takes data/clock/select pin values (N.B. should take an SPI definition?)
@@ -151,7 +300,7 @@ class SM16716Controller : public CLEDController {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
SPI mSPI;
- void writeHeader() {
+ void writeHeader() {
// Write out 50 zeros to the spi line (6 blocks of 8 followed by two single bit writes)
mSPI.select();
mSPI.writeBytesValueRaw(0, 6);
@@ -164,13 +313,13 @@ class SM16716Controller : public CLEDController {
public:
SM16716Controller() {}
- virtual void init() {
+ virtual void init() {
mSPI.init();
}
- virtual void clearLeds(int nLeds) {
+ virtual void clearLeds(int nLeds) {
mSPI.select();
- while(nLeds--) {
+ while(nLeds--) {
mSPI.template writeBit<0>(1);
mSPI.writeByte(0);
mSPI.writeByte(0);
@@ -181,39 +330,30 @@ public:
writeHeader();
}
- virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
- mSPI.select();
- uint8_t a = scale8(data[RGB_BYTE0(RGB_ORDER)], scale);
- uint8_t b = scale8(data[RGB_BYTE1(RGB_ORDER)], scale);
- uint8_t c = scale8(data[RGB_BYTE2(RGB_ORDER)], scale);
+protected:
- while(nLeds--) {
- mSPI.template writeBit<0>(1);
- mSPI.writeByte(a);
- mSPI.writeByte(b);
- mSPI.writeByte(c);
- }
+ virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) {
+ mSPI.template writePixels<FLAG_START_BIT, DATA_NOP, RGB_ORDER>(PixelController<RGB_ORDER>(data, nLeds, scale, getDither()));
writeHeader();
- mSPI.release();
}
- virtual void show(const struct CRGB *data, int nLeds, uint8_t scale = 255) {
+ virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
// Make sure the FLAG_START_BIT flag is set to ensure that an extra 1 bit is sent at the start
// of each triplet of bytes for rgb data
// writeHeader();
- mSPI.template writeBytes3<FLAG_START_BIT, RGB_ORDER>((byte*)data, nLeds * 3, scale);
+ mSPI.template writePixels<FLAG_START_BIT, DATA_NOP, RGB_ORDER>( PixelController<RGB_ORDER>(data, nLeds, scale, getDither()));
writeHeader();
}
#ifdef SUPPORT_ARGB
- virtual void show(const struct CARGB *data, int nLeds, uint8_t scale = 255) {
+ virtual void show(const struct CARGB *data, int nLeds, CRGB scale) {
mSPI.writeBytesValue(0, 6);
mSPI.template writeBit<0>(0);
mSPI.template writeBit<0>(0);
// Make sure the FLAG_START_BIT flag is set to ensure that an extra 1 bit is sent at the start
// of each triplet of bytes for rgb data
- mSPI.template writeBytes3<1 | FLAG_START_BIT, RGB_ORDER>((byte*)data, nLeds * 4, scale);
+ mSPI.template writePixels<FLAG_START_BIT, DATA_NOP, RGB_ORDER>(PixelController<RGB_ORDER>(data, nLeds, scale, getDither()));
}
#endif
};
@@ -224,76 +364,115 @@ public:
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#if defined(LIB8_ATTINY) && (F_CPU == 8000000)
-// WS2811@8Mhz 2 clocks, 5 clocks, 3 clocks
+// 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
+#define FMUL (F_CPU/8000000)
+// LPD1886
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class WS2811Controller800Khz : public ClocklessController_Trinket<DATA_PIN, 2, 5, 3, RGB_ORDER> {};
+class LPD1886Controller1250Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 3 * FMUL, 2 * FMUL, RGB_ORDER, 4> {};
+// WS2811@800khz 2 clocks, 5 clocks, 3 clocks
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class WS2811Controller400Khz : public ClocklessController_Trinket<DATA_PIN, 4, 10, 6, RGB_ORDER> {};
+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> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class UCS1903Controller400Khz : public ClocklessController_Trinket<DATA_PIN, 4, 12, 4, RGB_ORDER> {};
+class WS2811Controller400Khz : public ClocklessController<DATA_PIN, 4 * FMUL, 10 * FMUL, 6 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class TM1809Controller800Khz : public ClocklessController<DATA_PIN, 2, 5, 3, RGB_ORDER> {};
+class UCS1903Controller400Khz : public ClocklessController<DATA_PIN, 4 * FMUL, 12 * FMUL, 4 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class TM1803Controller400Khz : public ClocklessController<DATA_PIN, 6, 6, 6, RGB_ORDER> {};
-
-#elif defined(LIB8_ATTINY) && (F_CPU == 16000000)
+class UCS1903BController800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 4 * FMUL, 4 * FMUL, RGB_ORDER> {};
-// WS2811@16Mhz 4 clocks, 10 clocks, 6 clocks
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class WS2811Controller800Khz : public ClocklessController_Trinket<DATA_PIN, 4, 10, 6, RGB_ORDER> {};
+class TM1809Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class WS2811Controller400Khz : public ClocklessController_Trinket<DATA_PIN, 8, 20, 12, RGB_ORDER> {};
+class TM1803Controller400Khz : public ClocklessController<DATA_PIN, 6 * FMUL, 9 * FMUL, 6 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class UCS1903Controller400Khz : public ClocklessController_Trinket<DATA_PIN, 8, 24, 8, RGB_ORDER> {};
+class TM1829Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class TM1809Controller800Khz : public ClocklessController<DATA_PIN, 4, 10, 6, RGB_ORDER> {};
+class GW6205Controller400Khz : public ClocklessController<DATA_PIN, 6 * FMUL, 7 * FMUL, 6 * FMUL, RGB_ORDER, 4> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class TM1803Controller400Khz : public ClocklessController<DATA_PIN, 12, 12, 12, RGB_ORDER> {};
+class GW6205Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 4 * FMUL, 4 * FMUL, RGB_ORDER, 4> {};
#else
+// GW6205@400khz - 800ns, 800ns, 800ns
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class GW6205Controller400Khz : public ClocklessController<DATA_PIN, NS(800), NS(800), NS(800), RGB_ORDER, 4> {};
+#if NO_TIME(800, 800, 800)
+#warning "Not enough clock cycles available for the GW6205@400khz"
+#endif
+
+// GW6205@400khz - 400ns, 400ns, 400ns
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class GW6205Controller800Khz : public ClocklessController<DATA_PIN, NS(400), NS(400), NS(400), RGB_ORDER, 4> {};
+#if NO_TIME(400, 400, 400)
+#warning "Not enough clock cycles available for the GW6205@400khz"
+#endif
+
// UCS1903 - 500ns, 1500ns, 500ns
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"
+#if NO_TIME(500, 1500, 500)
+#warning "Not enough clock cycles available for the UCS103@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"
#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> {};
-#if NO_TIME(350, 350, 550)
+#if NO_TIME(350, 350, 550)
#warning "Not enough clock cycles available for the TM1809"
#endif
-// WS2811 - 400ns, 400ns, 450ns
+// WS2811 - 320ns, 320ns, 640ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class WS2811Controller800Khz : public ClocklessController<DATA_PIN, NS(400), NS(400), NS(450), RGB_ORDER> {};
-#if NO_TIME(400, 400, 450)
+class WS2811Controller800Khz : public ClocklessController<DATA_PIN, NS(320), NS(320), NS(640), RGB_ORDER> {};
+#if NO_TIME(320, 320, 640)
#warning "Not enough clock cycles available for the WS2811 (800khz)"
#endif
-// WS2811@400khz - 800ns, 800ns, 900ns
+// 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> {};
-#if NO_TIME(800, 800, 900)
+#if NO_TIME(800, 800, 900)
#warning "Not enough clock cycles available for the WS2811 (400Khz)"
#endif
// 750NS, 750NS, 750NS
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
-class TM1803Controller400Khz : public ClocklessController<DATA_PIN, NS(750), NS(750), NS(750), RGB_ORDER> {};
-#if NO_TIME(750, 750, 750)
-#warning "Not enough clock cycles available for the UCS103"
+class TM1803Controller400Khz : public ClocklessController<DATA_PIN, NS(700), NS(1100), NS(700), RGB_ORDER> {};
+#if NO_TIME(750, 750, 750)
+#warning "Not enough clock cycles available for the TM1803"
+#endif
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class TM1829Controller800Khz : public ClocklessController<DATA_PIN, NS(340), NS(340), NS(550), RGB_ORDER, 0, true, 500> {};
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class TM1829Controller1600Khz : public ClocklessController<DATA_PIN, NS(100), NS(300), NS(200), RGB_ORDER, 0, true, 500> {};
+#if NO_TIME(100, 300, 200)
+#warning "Not enough clock cycles available for TM1829@1.6Mhz"
#endif
+
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
+class LPD1886Controller1250Khz : public ClocklessController<DATA_PIN, NS(200), NS(400), NS(200), RGB_ORDER, 4> {};
+#if NO_TIME(200,400,200)
+#warning "Not enough clock cycles for LPD1886"
+#endif
+
#endif
#endif
diff --git a/clockless.h b/clockless.h
index 09ba6dda..b3fabbc5 100644
--- a/clockless.h
+++ b/clockless.h
@@ -3,281 +3,26 @@
#include "controller.h"
#include "lib8tion.h"
+#include "led_sysdefs.h"
#include "delay.h"
-#include <avr/interrupt.h> // for cli/se definitions
-
-// 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_LEAVING_R1_DIRTY(B, SCALE)
-#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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
+// 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.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, 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 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);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS((long)nLeds * 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);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS((long)nLeds * 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 = CLKS_TO_MICROS((long)nLeds * 24 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- sei();
- mWait.mark();
- }
-#endif
-
-#if defined(__MK20DX128__)
- inline static void write8Bits(register data_ptr_t port, register data_t hi, register data_t lo, register uint32_t & b) __attribute__ ((always_inline)) {
- // 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--) {
- FastPin<DATA_PIN>::fastset(port, hi);
- delaycycles<T1 - 5>(); // 5 cycles - 2 store, 1 and, 1 test, 1 if
- if(b & 0x80) { FastPin<DATA_PIN>::fastset(port, hi); } else { FastPin<DATA_PIN>::fastset(port, lo); }
- b <<= 1;
- delaycycles<T2 - 2>(); // 2 cycles, 1 store/skip, 1 shift
- FastPin<DATA_PIN>::fastset(port, lo);
- delaycycles<T3 - 5>(); // 3 cycles, 2 store, 1 sub, 1 branch backwards
- }
- // delay an extra cycle because falling out of the loop takes on less cycle than looping around
- delaycycles<1>();
-
- FastPin<DATA_PIN>::fastset(port, hi);
- delaycycles<T1 - 6>();
- if(b & 0x80) { FastPin<DATA_PIN>::fastset(port, hi); } else { FastPin<DATA_PIN>::fastset(port, lo); }
- delaycycles<T2 - 2>(); // 4 cycles, 2 store, store/skip
- 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.
- template<int SKIP, bool ADVANCE> static void showRGBInternal(register int nLeds, register uint8_t scale, register const byte *rgbdata) {
- register byte *data = (byte*)rgbdata;
- register data_t mask = FastPin<DATA_PIN>::mask();
- register data_ptr_t port = FastPin<DATA_PIN>::port();
- nLeds *= (3 + SKIP);
- register uint8_t *end = data + nLeds;
- register data_t hi = *port | mask;
- register data_t lo = *port & ~mask;
- *port = lo;
-
- register uint32_t b;
- b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE0(RGB_ORDER)];
- b = scale8(b, scale);
- while(data < end) {
- // Write first byte, read next byte
- write8Bits(port, hi, lo, b);
+// 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);
- b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE1(RGB_ORDER)];
- INLINE_SCALE(b, scale);
- delaycycles<T3 - 5>(); // 1 store, 2 load, 1 mul, 1 shift,
-
- // Write second byte
- write8Bits(port, hi, lo, b);
-
- b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE2(RGB_ORDER)];
- INLINE_SCALE(b, scale);
-
- data += 3 + SKIP;
- if((RGB_ORDER & 0070) == 0) {
- delaycycles<T3 - 6>(); // 1 store, 2 load, 1 mul, 1 shift, 1 adds if BRG or GRB
- } else {
- delaycycles<T3 - 5>(); // 1 store, 2 load, 1 mul, 1 shift,
- }
-
- // Write third byte
- write8Bits(port, hi, lo, b);
-
- b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE0(RGB_ORDER)];
- INLINE_SCALE(b, scale);
-
- delaycycles<T3 - 11>(); // 1 store, 2 load (with increment), 1 mul, 1 shift, 1 cmp, 1 branch backwards, 1 movim
- };
- }
-
-#else // AVR loop/implementation
-
- template <int N, int ADJ>inline static void bitSetLast(register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t b) {
- // First cycle
- FastPin<DATA_PIN>::fastset(port, hi); // 1 clock cycle if using out, 2 otherwise
- delaycycles<T1 - (_CYCLES(DATA_PIN))>(); // 1st cycle length minus 1 clock for out, 1 clock for sbrs
- __asm__ __volatile__ ("sbrs %0, %1" :: "r" (b), "M" (N) :); // 1 clock for check (+1 if skipping, next op is also 1 clock)
-
- // Second cycle
- FastPin<DATA_PIN>::fastset(port, lo); // 1/2 clock cycle if using out
- delaycycles<T2 - (_CYCLES(DATA_PIN))>(); // 2nd cycle length minus 1/2 clock for out
-
- // Third cycle
- FastPin<DATA_PIN>::fastset(port, lo); // 1/2 clock cycle if using out
- delaycycles<T3 - (_CYCLES(DATA_PIN) + ADJ)>(); // 3rd cycle length minus the passed in adjustment
- }
-
- // 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 byte *data = (byte*)rgbdata;
- register data_t mask = FastPin<DATA_PIN>::mask();
- register data_ptr_t port = FastPin<DATA_PIN>::port();
- nLeds *= (3 + SKIP);
- register uint8_t *end = data + nLeds;
- register data_t hi = *port | mask;
- register data_t lo = *port & ~mask;
- *port = lo;
-
-#if 0
- register uint8_t b = *data++;
- while(data <= end) {
- bitSetFast<7>(port, hi, lo, b);
- bitSetFast<6>(port, hi, lo, b);
- bitSetFast<5>(port, hi, lo, b);
- bitSetFast<4>(port, hi, lo, b);
- bitSetFast<3>(port, hi, lo, b);
- // Leave an extra 2 clocks for the next byte load
- bitSetLast<2, 2>(port, hi, lo, b);
- register uint8_t next = *data++;
- // Leave an extra 4 clocks for the scale
- bitSetLast<1, 4>(port, hi, lo, b);
- next = scale8(next, scale);
- bitSetLast<0, END_OF_LOOP>(port, hi, lo, b);
- b = next;
- }
-#else
- register uint8_t b;
-
- if(ADVANCE) {
- b = data[SKIP + RGB_BYTE0(RGB_ORDER)];
- } else {
- b = rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
- }
- b = scale8_LEAVING_R1_DIRTY(b, scale);
-
- register uint8_t c;
- register uint8_t d;
- while(data < end) {
- for(register byte x=5; x; x--) {
- bitSetLast<7, 4>(port, hi, lo, b);
- b <<= 1;
- }
- delaycycles<1>();
- bitSetLast<7, 1>(port, hi, lo, b);
- delaycycles<1>();
-
- // Leave an extra 4 clocks for the load/scale
- bitSetLast<6, 6>(port, hi, lo, b);
- if(ADVANCE) {
- c = data[SKIP + RGB_BYTE1(RGB_ORDER)];
- } else {
- c = rgbdata[SKIP + RGB_BYTE1(RGB_ORDER)];
- delaycycles<1>();
- }
- INLINE_SCALE(c, scale);
- bitSetLast<5, 1>(port, hi, lo, b);
-
- for(register byte x=5; x; x--) {
- bitSetLast<7, 4>(port, hi, lo, c);
- c <<= 1;
- }
- delaycycles<1>();
- bitSetLast<7, 1>(port, hi, lo, c);
- delaycycles<1>();
-
- // Leave an extra 4 clocks for the load/scale
- bitSetLast<6, 6>(port, hi, lo, c);
- if(ADVANCE) {
- d = data[SKIP + RGB_BYTE2(RGB_ORDER)];
- } else {
- d = rgbdata[SKIP + RGB_BYTE2(RGB_ORDER)];
- delaycycles<1>();
- }
- INLINE_SCALE(d, scale);
- bitSetLast<5, 1>(port, hi, lo, c);
-
- for(register byte x=5; x; x--) {
- bitSetLast<7, 4>(port, hi, lo, d);
- d <<= 1;
- }
- delaycycles<1>();
- // Leave an extra clock for the data pointer advance
- bitSetLast<7, 2>(port, hi, lo, d);
- data += (SKIP + 3);
- // Leave an extra 4 clocks for the load/scale
- bitSetLast<6, 6>(port, hi, lo, d);
- if(ADVANCE) {
- b = data[SKIP + RGB_BYTE0(RGB_ORDER)];
- } else {
- b = rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
- delaycycles<1>();
- }
- INLINE_SCALE(b, scale);
- bitSetLast<5, 6>(port, hi, lo, d);
- }
- cleanup_R1();
-#endif
- }
-#endif
-#ifdef SUPPORT_ARGB
- virtual void showARGB(struct CARGB *data, int nLeds) {
- // TODO: IMPLEMENTME
- }
-#endif
-};
+// #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
new file mode 100644
index 00000000..c48cde5d
--- /dev/null
+++ b/clockless2.h
@@ -0,0 +1,241 @@
+#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_k20.h b/clockless_arm_k20.h
new file mode 100644
index 00000000..126955b1
--- /dev/null
+++ b/clockless_arm_k20.h
@@ -0,0 +1,144 @@
+#ifndef __INC_CLOCKLESS_ARM_K20_H
+#define __INC_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)
+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 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 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();
+
+ uint32_t clocks = showRGBInternal(pixels);
+
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS(clocks);
+ MS_COUNTER += (1 + (microsTaken / 1000));
+ 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();
+
+ uint32_t clocks = showRGBInternal(pixels);
+
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS(clocks);
+ MS_COUNTER += (1 + (microsTaken / 1000));
+ 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();
+
+ uint32_t clocks = showRGBInternal(pixels);
+
+
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS(clocks);
+ MS_COUNTER += (1 + (microsTaken / 1000));
+ 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 data_t hi, register data_t lo, register uint8_t & b) {
+ for(register uint32_t i = BITS-1; i > 0; i--) {
+ while(ARM_DWT_CYCCNT < next_mark);
+ next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
+ FastPin<DATA_PIN>::fastset(port, hi);
+ if(b&0x80) {
+ while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
+ FastPin<DATA_PIN>::fastset(port, lo);
+ } else {
+ while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
+ FastPin<DATA_PIN>::fastset(port, lo);
+ }
+ b <<= 1;
+ }
+
+ while(ARM_DWT_CYCCNT < next_mark);
+ next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
+ FastPin<DATA_PIN>::fastset(port, hi);
+
+ if(b&0x80) {
+ while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
+ FastPin<DATA_PIN>::fastset(port, lo);
+ } else {
+ while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
+ 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
+ ARM_DEMCR |= ARM_DEMCR_TRCENA;
+ ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
+ ARM_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();
+
+ 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, 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();
+ };
+
+ return ARM_DWT_CYCCNT;
+ }
+};
+#endif
+
+#endif
diff --git a/clockless_arm_sam.h b/clockless_arm_sam.h
new file mode 100644
index 00000000..74a0f602
--- /dev/null
+++ b/clockless_arm_sam.h
@@ -0,0 +1,217 @@
+#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
index 168ba670..4f7b7db3 100644
--- a/clockless_trinket.h
+++ b/clockless_trinket.h
@@ -6,128 +6,171 @@
#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) {
+template<int _LOOP, int PAD> inline void _dc_AVR(register uint8_t & loopvar) {
_dc<PAD>(loopvar);
- asm __volatile__ ( "LDI %[loopvar], %[_LOOP]\n\tL_%=: DEC %[loopvar]\n\t BRNE L_%=\n\t" :
+ // 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/3,CYCLES%3>(loopvar);
+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__("cp r0,r0":::);}
+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-(2+ADJ)>(loopvar);
-#define D2(ADJ) _dc<T2-(1+ADJ)>(loopvar);
-#define D3(ADJ) _dc<T3-(1+ADJ)>(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
+// 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 WAIT_TIME = 50>
-class ClocklessController_Trinket : public CLEDController {
+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() {
+ virtual void init() {
FastPin<DATA_PIN>::setOutput();
}
virtual void clearLeds(int nLeds) {
- static byte zeros[3] = {0,0,0};
- showRGBInternal_AdjTime(0, false, nLeds, 0, zeros);
+ 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 & data, int nLeds, uint8_t scale = 255) {
- showRGBInternal_AdjTime(0, false, nLeds, scale, (const byte*)&data);
+ 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, uint8_t scale = 255) {
- showRGBInternal_AdjTime(0, true, nLeds, scale, (const byte*)rgbdata);
+ 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, uint8_t scale = 255) {
- showRGBInternal_AdjTime(1, true, nLeds, scale, (const byte*)rgbdata);
+ virtual void show(const struct CARGB *rgbdata, int nLeds, CRGB scale) {
+ showAdjTime((uint8_t*)rgbdata, nLeds, scale, true, 1);
}
#endif
- void __attribute__ ((noinline)) showRGBInternal_AdjTime(int skip, bool advance, int nLeds, uint8_t scale, const byte *rgbdata) {
+ 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(skip, advance, nLeds, scale, rgbdata);
-
+ showRGBInternal(pixels);
// Adjust the timer
- long microsTaken = CLKS_TO_MICROS((long)nLeds * 24 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
+#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 */ \
- [b0] "+r" (b0), \
- [b1] "+r" (b1), \
- [b2] "+r" (b2), \
[count] "+x" (count), \
- [scale_base] "+r" (scale_base), \
[data] "+z" (data), \
- [loopvar] "+a" (loopvar) \
+ [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), \
- [scale] "r" (scale), \
- [ADV] "r" (advanceBy), \
- [zero] "r" (zero), \
+ [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)), \
- [PORT] "M" (FastPin<DATA_PIN>::port()-0x20) \
- : /* clobber registers */
+ [O2] "M" (RGB_BYTE2(RGB_ORDER)) \
+ : /* clobber registers */
// 1 cycle, write hi to the port
-#define HI1 asm __volatile__("out %[PORT], %[hi]" ASM_VARS );
+#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 asm __volatile__("out %[PORT], %[lo]" ASM_VARS );
+#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 );
-// 3 cycles - load a byte from ram into the scaling scratch space with the given offset, clear the target var
-#define LDSCL3(B,O) asm __volatile__("ldd %[scale_base], Z + %[" #O "]\n\tclr %[" #B "]" ASM_VARS );
-// 2 cycles - increment the data pointer
-#define IDATA2 asm __volatile__("add %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]" 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 );
+// 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 SCALE2(B, N) asm __volatile__("sbrc %[scale], " #N "\n\tadd %[" #B "], %[scale_base]" ASM_VARS );
+#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
@@ -135,145 +178,186 @@ public:
// 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 RORSC4(B, N) ROR1(B) CLC1 SCALE2(B, N)
+#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 SCROR4(B, N) SCALE2(B,N) ROR1(B) CLC1
+#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 );
-// delay time
+// 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 );
- // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+#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__ ((noinline)) showRGBInternal(int skip, bool advance, int nLeds, uint8_t scale, const byte *rgbdata) {
- byte *data = (byte*)rgbdata;
- data_t mask = FastPin<DATA_PIN>::mask();
+ 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();
- // register uint8_t *end = data + nLeds;
+ 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, b1, b2;
- uint16_t count = nLeds;
- uint8_t scale_base = 0;
- uint16_t advanceBy = advance ? (skip+3) : 0;
- const uint8_t zero = 0;
- b0 = data[RGB_BYTE0(RGB_ORDER)];
- b0 = scale8(b0, scale);
- b1 = data[RGB_BYTE1(RGB_ORDER)];
- b2 = 0;
- register uint8_t loopvar;
-
- if(RGB_ORDER == RGB) {
- // If the rgb order is RGB, we can cut back on program space usage by making a much more compact
- // representation.
-
- // multiply count by 3, don't use * because there's no hardware multiply
- count = count+(count<<1);
- advanceBy = advance ? 1 : 0;
- {
- /* asm */
- LOOP
- // Sum of the clock counts across each row should be 10 for 8Mhz, WS2811
-#if TRINKET_SCALE
- // Inline scaling, RGB ordering matches byte ordering
- HI1 D1(0) QLO2(b0, 7) LDSCL3(b1,O1) D2(3) LO1 SCALE2(b1,0) D3(2)
- HI1 D1(0) QLO2(b0, 6) RORSC4(b1,1) D2(4) LO1 ROR1(b1) CLC1 D3(2)
- HI1 D1(0) QLO2(b0, 5) SCROR4(b1,2) D2(4) LO1 SCALE2(b1,3) D3(2)
- HI1 D1(0) QLO2(b0, 4) RORSC4(b1,4) D2(4) LO1 ROR1(b1) CLC1 D3(2)
- HI1 D1(0) QLO2(b0, 3) SCROR4(b1,5) D2(4) LO1 SCALE2(b1,6) D3(2)
- HI1 D1(0) QLO2(b0, 2) RORSC4(b1,7) D2(4) LO1 ROR1(b1) CLC1 D3(2)
- HI1 D1(0) QLO2(b0, 1) IDATA2 D2(2) LO1 D3(0)
- // In the last bit's first block, we decrement and branch to done if we decremented to 0
- HI1 D1(0) QLO2(b0, 0) DCOUNT2 BRLOOP1 MOV1(b0, b1) D2(4) LO1 D3(2) JMPLOOP2
-#else
- // no inline scaling, RGB ordering matches byte ordering
- HI1 D1(0) QLO2(b0, 7) LD2(b1,O1) D2(2) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 6) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 5) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 4) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 3) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 2) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 1) IDATA2 D2(2) LO1 D3(0)
- // In the last bit's first block, we decrement and branch to done if we decremented to 0
- HI1 D1(0) QLO2(b2, 0) DCOUNT2 BRLOOP1 MOV1(b0,b1) D2(4) LO1 D3(2) JMPLOOP2
-#endif
- DONE
- D2(4) LO1 D3(0)
- }
- }
- else
+ 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--)
{
- /* asm */
- LOOP
+ // 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);
+
// 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(0) QLO2(b0, 7) LDSCL3(b1,O1) D2(3) LO1 SCALE2(b1,0) D3(2)
- HI1 D1(0) QLO2(b0, 6) RORSC4(b1,1) D2(4) LO1 ROR1(b1) CLC1 D3(2)
- HI1 D1(0) QLO2(b0, 5) SCROR4(b1,2) D2(4) LO1 SCALE2(b1,3) D3(2)
- HI1 D1(0) QLO2(b0, 4) RORSC4(b1,4) D2(4) LO1 ROR1(b1) CLC1 D3(2)
- HI1 D1(0) QLO2(b0, 3) SCROR4(b1,5) D2(4) LO1 SCALE2(b1,6) D3(2)
- HI1 D1(0) QLO2(b0, 2) RORSC4(b1,7) D2(4) LO1 ROR1(b1) CLC1 D3(2)
- HI1 D1(0) QLO2(b0, 1) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 0) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b1, 7) LDSCL3(b2,O2) D2(3) LO1 SCALE2(b2,0) D3(2)
- HI1 D1(0) QLO2(b1, 6) RORSC4(b2,1) D2(4) LO1 ROR1(b2) CLC1 D3(2)
- HI1 D1(0) QLO2(b1, 5) SCROR4(b2,2) D2(4) LO1 SCALE2(b2,3) D3(2)
- HI1 D1(0) QLO2(b1, 4) RORSC4(b2,4) D2(4) LO1 ROR1(b2) CLC1 D3(2)
- HI1 D1(0) QLO2(b1, 3) SCROR4(b2,5) D2(4) LO1 SCALE2(b2,6) D3(2)
- HI1 D1(0) QLO2(b1, 2) RORSC4(b2,7) D2(4) LO1 ROR1(b2) CLC1 D3(2)
- HI1 D1(0) QLO2(b1, 1) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b1, 0) IDATA2 D2(2) LO1 D3(0)
- HI1 D1(0) QLO2(b2, 7) LDSCL3(b0,O0) D2(3) LO1 SCALE2(b0,0) D3(2)
- HI1 D1(0) QLO2(b2, 6) RORSC4(b0,1) D2(4) LO1 ROR1(b0) CLC1 D3(2)
- HI1 D1(0) QLO2(b2, 5) SCROR4(b0,2) D2(4) LO1 SCALE2(b0,3) D3(2)
- HI1 D1(0) QLO2(b2, 4) RORSC4(b0,4) D2(4) LO1 ROR1(b0) CLC1 D3(2)
- HI1 D1(0) QLO2(b2, 3) SCROR4(b0,5) D2(4) LO1 SCALE2(b0,6) D3(2)
- HI1 D1(0) QLO2(b2, 2) RORSC4(b0,7) D2(4) LO1 ROR1(b0) CLC1 D3(2)
- HI1 D1(0) QLO2(b2, 1) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b2, 0) DCOUNT2 BRLOOP1 D2(3) LO1 D3(2) JMPLOOP2
+ 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(0) QLO2(b0, 7) LD2(b1,O1) D2(2) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 6) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 5) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 4) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 3) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 2) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 1) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b0, 0) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b1, 7) LD2(b2,O2) D2(2) LO1 D3(0)
- HI1 D1(0) QLO2(b1, 6) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b1, 5) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b1, 4) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b1, 3) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b1, 2) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b1, 1) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b1, 0) IDATA2 D2(2) LO1 D3(0)
- HI1 D1(0) QLO2(b2, 7) LD2(b0,O0) D2(2) LO1 D3(0)
- HI1 D1(0) QLO2(b2, 6) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b2, 5) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b2, 4) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b2, 3) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b2, 2) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b2, 1) D2(0) LO1 D3(0)
- HI1 D1(0) QLO2(b2, 0) DCOUNT2 BRLOOP1 D2(3) LO1 D3(2) JMPLOOP2
-#endif
- DONE
- D2(4) LO1 D3(0)
+ 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) {
+ virtual void showARGB(struct CARGB *data, int nLeds) {
// TODO: IMPLEMENTME
}
#endif
};
#endif
+
+#endif
diff --git a/color.h b/color.h
new file mode 100644
index 00000000..17d1b20a
--- /dev/null
+++ b/color.h
@@ -0,0 +1,54 @@
+#ifndef __INC_COLOR_H
+#define __INC_COLOR_H
+
+// definitions for color correction and light temperatures
+
+typedef enum {
+ // Color correction starting points
+
+ // 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
+ Typical8mmPixel=0xFFE08C /* 255, 224, 140 */,
+ TypicalPixelString=0xFFE08C /* 255, 224, 140 */,
+
+ // 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'
+ Candle=0xFF9329 /* 1900 K, 255, 147, 41 */,
+ Tungsten40W=0xFFC58F /* 2600 K, 255, 197, 143 */,
+ Tungsten100W=0xFFD6AA /* 2850 K, 255, 214, 170 */,
+ Halogen=0xFFF1E0 /* 3200 K, 255, 241, 224 */,
+ CarbonArc=0xFFFAF4 /* 5200 K, 255, 250, 244 */,
+ HighNoonSun=0xFFFFFB /* 5400 K, 255, 255, 251 */,
+ DirectSunlight=0xFFFFFF /* 6000 K, 255, 255, 255 */,
+ OvercastSky=0xC9E2FF /* 7000 K, 201, 226, 255 */,
+ 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.
+ WarmFluorescent=0xFFF4E5 /* 0 K, 255, 244, 229 */,
+ StandardFluorescent=0xF4FFFA /* 0 K, 244, 255, 250 */,
+ CoolWhiteFluorescent=0xD4EBFF /* 0 K, 212, 235, 255 */,
+ FullSpectrumFluorescent=0xFFF4F2 /* 0 K, 255, 244, 242 */,
+ GrowLightFluorescent=0xFFEFF7 /* 0 K, 255, 239, 247 */,
+ BlackLightFluorescent=0xA700FF /* 0 K, 167, 0, 255 */,
+ MercuryVapor=0xD8F7FF /* 0 K, 216, 247, 255 */,
+ SodiumVapor=0xFFD1B2 /* 0 K, 255, 209, 178 */,
+ MetalHalide=0xF2FCFF /* 0 K, 242, 252, 255 */,
+ HighPressureSodium=0xFFB74C /* 0 K, 255, 183, 76 */,
+
+ // Uncorrected temperature 0xFFFFFF
+ UncorrectedTemperature=0xFFFFFF
+} ColorTemperature;
+#endif \ No newline at end of file
diff --git a/colorpalettes.cpp b/colorpalettes.cpp
new file mode 100644
index 00000000..8cec8980
--- /dev/null
+++ b/colorpalettes.cpp
@@ -0,0 +1,164 @@
+#ifndef __INC_COLORPALETTES_H
+#define __INC_COLORPALETTES_H
+
+#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
+
+
+
+// Preset color schemes, such as they are.
+
+// 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 =
+{
+ 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 =
+{
+ 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,
+ CRGB::DarkRed
+};
+
+
+extern const TProgmemRGBPalette16 OceanColors_p 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 =
+{
+ 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 =
+{
+ 0xFF0000, 0xD52A00, 0xAB5500, 0xAB7F00,
+ 0xABAB00, 0x56D500, 0x00FF00, 0x00D52A,
+ 0x00AB55, 0x0056AA, 0x0000FF, 0x2A00D5,
+ 0x5500AB, 0x7F0081, 0xAB0055, 0xD5002B
+};
+
+// HSV Rainbow colors with alternatating stripes of black
+#define RainbowStripesColors_p RainbowStripeColors_p
+extern const TProgmemRGBPalette16 RainbowStripeColors_p PROGMEM =
+{
+ 0xFF0000, 0x000000, 0xAB5500, 0x000000,
+ 0xABAB00, 0x000000, 0x00FF00, 0x000000,
+ 0x00AB55, 0x000000, 0x0000FF, 0x000000,
+ 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 =
+{
+ 0x5500AB, 0x84007C, 0xB5004B, 0xE5001B,
+ 0xE81700, 0xB84700, 0xAB7700, 0xABAB00,
+ 0xAB5500, 0xDD2200, 0xF2000E, 0xC2003E,
+ 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 =
+{
+ 0x000000,
+ 0x330000, 0x660000, 0x990000, 0xCC0000, 0xFF0000,
+ 0xFF3300, 0xFF6600, 0xFF9900, 0xFFCC00, 0xFFFF00,
+ 0xFFFF33, 0xFFFF66, 0xFFFF99, 0xFFFFCC, 0xFFFFFF
+};
+
+#endif
diff --git a/colorpalettes.h b/colorpalettes.h
new file mode 100644
index 00000000..340349d4
--- /dev/null
+++ b/colorpalettes.h
@@ -0,0 +1,42 @@
+#ifndef __INC_COLORPALETTES_H
+#define __INC_COLORPALETTES_H
+
+#include "colorutils.h"
+
+
+// Preset color schemes, such as they are.
+
+// 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;
+
+// HSV Rainbow
+extern const TProgmemRGBPalette16 RainbowColors_p 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;
+
+#endif
diff --git a/colorutils.cpp b/colorutils.cpp
new file mode 100644
index 00000000..995db69e
--- /dev/null
+++ b/colorutils.cpp
@@ -0,0 +1,579 @@
+#define __PROG_TYPES_COMPAT__
+
+#include <stdint.h>
+
+#include "hsv2rgb.h"
+#include "colorutils.h"
+
+
+
+
+void fill_solid( struct CRGB * leds, int numToFill,
+ const struct CRGB& color)
+{
+ for( int i = 0; i < numToFill; i++) {
+ leds[i] = color;
+ }
+}
+
+void fill_solid( struct CHSV * targetArray, int numToFill,
+ const struct CHSV& hsvColor)
+{
+ for( int i = 0; i < numToFill; i++) {
+ targetArray[i] = hsvColor;
+ }
+}
+
+
+// void fill_solid( struct CRGB* targetArray, int numToFill,
+// const struct CHSV& hsvColor)
+// {
+// fill_solid<CRGB>( targetArray, numToFill, (CRGB) hsvColor);
+// }
+
+void fill_rainbow( struct CRGB * pFirstLED, int numToFill,
+ uint8_t initialhue,
+ uint8_t deltahue )
+{
+ CHSV hsv;
+ hsv.hue = initialhue;
+ hsv.val = 255;
+ hsv.sat = 255;
+ for( int i = 0; i < numToFill; i++) {
+ hsv2rgb_rainbow( hsv, pFirstLED[i]);
+ hsv.hue += deltahue;
+ }
+}
+
+void fill_rainbow( struct CHSV * targetArray, int numToFill,
+ uint8_t initialhue,
+ uint8_t deltahue )
+{
+ CHSV hsv;
+ hsv.hue = initialhue;
+ hsv.val = 255;
+ hsv.sat = 255;
+ for( int i = 0; i < numToFill; i++) {
+ targetArray[i] = hsv;
+ hsv.hue += deltahue;
+ }
+}
+
+
+void fill_gradient_RGB( CRGB* leds,
+ uint16_t startpos, CRGB startcolor,
+ uint16_t endpos, CRGB endcolor )
+{
+ // if the points are in the wrong order, straighten them
+ if( endpos < startpos ) {
+ uint16_t t = endpos;
+ CRGB tc = endcolor;
+ startpos = t;
+ startcolor = tc;
+ endcolor = startcolor;
+ endpos = startpos;
+ }
+
+ 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;
+
+ accum88 r88 = startcolor.r << 8;
+ accum88 g88 = startcolor.g << 8;
+ accum88 b88 = startcolor.b << 8;
+ for( uint16_t i = startpos; i <= endpos; i++) {
+ leds[i] = CRGB( r88 >> 8, g88 >> 8, b88 >> 8);
+ r88 += rdelta87;
+ g88 += gdelta87;
+ b88 += bdelta87;
+ }
+}
+
+#if 0
+void fill_gradient( const CHSV& c1, const CHSV& c2)
+{
+ fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2);
+}
+
+void fill_gradient( const CHSV& c1, const CHSV& c2, const CHSV& c3)
+{
+ fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3);
+}
+
+void fill_gradient( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4)
+{
+ fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3, c4);
+}
+
+void fill_gradient_RGB( const CRGB& c1, const CRGB& c2)
+{
+ fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2);
+}
+
+void fill_gradient_RGB( const CRGB& c1, const CRGB& c2, const CRGB& c3)
+{
+ fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3);
+}
+
+void fill_gradient_RGB( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4)
+{
+ fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3, c4);
+}
+#endif
+
+
+
+
+void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2)
+{
+ uint16_t last = numLeds - 1;
+ fill_gradient_RGB( leds, 0, c1, last, c2);
+}
+
+
+void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3)
+{
+ uint16_t half = (numLeds / 2);
+ uint16_t last = numLeds - 1;
+ fill_gradient_RGB( leds, 0, c1, half, c2);
+ fill_gradient_RGB( leds, half, c2, last, c3);
+}
+
+void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4)
+{
+ uint16_t onethird = (numLeds / 3);
+ uint16_t twothirds = ((numLeds * 2) / 3);
+ uint16_t last = numLeds - 1;
+ fill_gradient_RGB( leds, 0, c1, onethird, c2);
+ fill_gradient_RGB( leds, onethird, c2, twothirds, c3);
+ fill_gradient_RGB( leds, twothirds, c3, last, c4);
+}
+
+
+
+
+void nscale8_video( CRGB* leds, uint16_t num_leds, uint8_t scale)
+{
+ for( uint16_t i = 0; i < num_leds; i++) {
+ leds[i].nscale8_video( scale);
+ }
+}
+
+void fade_video(CRGB* leds, uint16_t num_leds, uint8_t fadeBy)
+{
+ nscale8_video( leds, num_leds, 255 - fadeBy);
+}
+
+void fadeLightBy(CRGB* leds, uint16_t num_leds, uint8_t fadeBy)
+{
+ nscale8_video( leds, num_leds, 255 - fadeBy);
+}
+
+
+void fadeToBlackBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy)
+{
+ nscale8( leds, num_leds, 255 - fadeBy);
+}
+
+void fade_raw( CRGB* leds, uint16_t num_leds, uint8_t fadeBy)
+{
+ nscale8( leds, num_leds, 255 - fadeBy);
+}
+
+void nscale8_raw( CRGB* leds, uint16_t num_leds, uint8_t scale)
+{
+ nscale8( leds, num_leds, scale);
+}
+
+void nscale8( CRGB* leds, uint16_t num_leds, uint8_t scale)
+{
+ for( uint16_t i = 0; i < num_leds; i++) {
+ leds[i].nscale8( scale);
+ }
+}
+
+
+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;
+}
+
+
+
+void nblend( CRGB* existing, CRGB* overlay, uint16_t count, fract8 amountOfOverlay)
+{
+ for( uint16_t i = count; i; i--) {
+ nblend( *existing, *overlay, amountOfOverlay);
+ existing++;
+ overlay++;
+ }
+}
+
+CRGB blend( const CRGB& p1, const CRGB& p2, fract8 amountOfP2 )
+{
+ CRGB nu(p1);
+ nblend( nu, p2, amountOfP2);
+ return nu;
+}
+
+CRGB* blend( const CRGB* src1, const CRGB* src2, CRGB* dest, uint16_t count, fract8 amountOfsrc2 )
+{
+ for( uint16_t i = count; i; i--) {
+ dest[i] = blend(src1[i], src2[i], amountOfsrc2);
+ }
+ return dest;
+}
+
+
+
+CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay, TGradientDirectionCode directionCode)
+{
+ 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);
+ }
+ else /* directionCode == BACKWARD_HUES */
+ {
+ 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;
+}
+
+
+
+void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOverlay, TGradientDirectionCode directionCode )
+{
+ for( uint16_t i = count; i; i--) {
+ nblend( *existing, *overlay, amountOfOverlay, directionCode);
+ existing++;
+ overlay++;
+ }
+}
+
+CHSV blend( const CHSV& p1, const CHSV& p2, fract8 amountOfP2, TGradientDirectionCode directionCode )
+{
+ CHSV nu(p1);
+ nblend( nu, p2, amountOfP2, directionCode);
+ return nu;
+}
+
+CHSV* blend( const CHSV* src1, const CHSV* src2, CHSV* dest, uint16_t count, fract8 amountOfsrc2, TGradientDirectionCode directionCode )
+{
+ for( uint16_t i = count; i; i--) {
+ dest[i] = blend(src1[i], src2[i], amountOfsrc2, directionCode);
+ }
+ return dest;
+}
+
+
+
+// CRGB HeatColor( uint8_t temperature)
+//
+// Approximates a 'black body radiation' spectrum for
+// a given 'heat' level. This is useful for animations of 'fire'.
+// Heat is specified as an arbitrary scale from 0 (cool) to 255 (hot).
+// This is NOT a chromatically correct 'black body radiation'
+// spectrum, but it's surprisingly close, and it's fast and small.
+//
+// On AVR/Arduino, this typically takes around 70 bytes of program memory,
+// versus 768 bytes for a full 256-entry RGB lookup table.
+
+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;
+}
+
+
+
+CRGB ColorFromPalette( const CRGBPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType)
+{
+ 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;
+ 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)
+{
+ const CRGB* entry = &(pal[0]) + index;
+
+ 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);
+}
+
+
+CHSV ColorFromPalette( const struct CHSVPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType)
+{
+ 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;
+
+ // Now some special casing for blending to or from
+ // either black or white. Black and white don't have
+ // proper 'hue' of their own, so when ramping from
+ // something else to/from black/white, we set the 'hue'
+ // of the black/white color to be the same as the hue
+ // 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.
+ sat1 += sat2;
+ val1 += val2;
+
+ uint8_t deltaHue = (uint8_t)(hue2 - hue1);
+ if( deltaHue & 0x80 ) {
+ // go backwards
+ hue1 -= scale8( 256 - deltaHue, f2);
+ } else {
+ // go forwards
+ hue1 += scale8( deltaHue, f2);
+ }
+
+ cleanup_R1();
+ }
+
+ if( brightness != 255) {
+ val1 = scale8_video( val1, brightness);
+ }
+
+ return CHSV( hue1, sat1, val1);
+}
+
+
+CHSV ColorFromPalette( const struct CHSVPalette256& pal, uint8_t index, uint8_t brightness, TBlendType)
+{
+ CHSV hsv;// = *( &(pal[0]) + index );
+
+ if( brightness != 255) {
+ hsv.value = scale8_video( hsv.value, brightness);
+ }
+
+ return hsv;
+}
+
+
+void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette256& destpal256)
+{
+ for( int i = 0; i < 256; i++) {
+ destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal16, i);
+ }
+}
+
+void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette256& destpal256)
+{
+ for( int i = 0; i < 256; i++) {
+ destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal16, i);
+ }
+}
+
+#if 0
+// replaced by PartyColors_p
+void SetupPartyColors(CRGBPalette16& pal)
+{
+ fill_gradient( pal, 0, CHSV( HUE_PURPLE,255,255), 7, CHSV(HUE_YELLOW - 18,255,255), FORWARD_HUES);
+ fill_gradient( pal, 8, CHSV( HUE_ORANGE,255,255), 15, CHSV(HUE_BLUE + 18,255,255), BACKWARD_HUES);
+}
+#endif
diff --git a/colorutils.h b/colorutils.h
new file mode 100644
index 00000000..55772c7f
--- /dev/null
+++ b/colorutils.h
@@ -0,0 +1,804 @@
+#ifndef __INC_COLORUTILS_H
+#define __INC_COLORUTILS_H
+
+#include <avr/pgmspace.h>
+
+#include "pixeltypes.h"
+
+
+// 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);
+
+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)
+void fill_rainbow( struct CRGB * pFirstLED, int numToFill,
+ uint8_t initialhue,
+ uint8_t deltahue = 5);
+
+void fill_rainbow( struct CHSV * targetArray, int numToFill,
+ uint8_t initialhue,
+ uint8_t deltahue = 5);
+
+
+// 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.
+
+typedef enum { FORWARD_HUES, BACKWARD_HUES, SHORTEST_HUES, LONGEST_HUES } TGradientDirectionCode;
+
+
+
+#define saccum87 int16_t
+
+template <typename T>
+void fill_gradient( T* targetArray,
+ uint16_t startpos, CHSV startcolor,
+ uint16_t endpos, CHSV endcolor,
+ TGradientDirectionCode directionCode = SHORTEST_HUES )
+{
+ // if the points are in the wrong order, straighten them
+ if( endpos < startpos ) {
+ uint16_t t = endpos;
+ CHSV tc = endcolor;
+ startpos = t;
+ startcolor = tc;
+ endcolor = startcolor;
+ endpos = startpos;
+ }
+
+ // 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
+ // of what 'hue' was set in the endcolor (since it doesn't matter)
+ 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
+ // of what 'hue' was set in the startcolor (since it doesn't matter)
+ 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;
+ }
+ else /* directionCode == BACKWARD_HUES */
+ {
+ 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;
+ for( uint16_t i = startpos; i <= endpos; i++) {
+ targetArray[i] = CHSV( hue88 >> 8, sat88 >> 8, val88 >> 8);
+ hue88 += huedelta87;
+ sat88 += satdelta87;
+ val88 += valdelta87;
+ }
+}
+
+
+// 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,
+ TGradientDirectionCode directionCode = SHORTEST_HUES )
+{
+ uint16_t last = numLeds - 1;
+ fill_gradient( targetArray, 0, c1, last, c2, directionCode);
+}
+
+template <typename T>
+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);
+ uint16_t last = numLeds - 1;
+ fill_gradient( targetArray, 0, c1, half, c2, directionCode);
+ fill_gradient( targetArray, half, c2, last, c3, directionCode);
+}
+
+template <typename T>
+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);
+ uint16_t twothirds = ((numLeds * 2) / 3);
+ uint16_t last = numLeds - 1;
+ fill_gradient( targetArray, 0, c1, onethird, c2, directionCode);
+ fill_gradient( targetArray, onethird, c2, twothirds, c3, directionCode);
+ fill_gradient( targetArray, twothirds, c3, last, c4, directionCode);
+}
+
+// convenience synonym
+#define fill_gradient_HSV fill_gradient
+
+
+// fill_gradient_RGB - fill a range of LEDs with a smooth RGB gradient
+// between two specified RGB colors.
+// Unlike HSV, there is no 'color wheel' in RGB space,
+// and therefore there's only one 'direction' for the
+// gradient to go, and no 'direction code' is needed.
+void fill_gradient_RGB( CRGB* leds,
+ uint16_t startpos, CRGB startcolor,
+ uint16_t endpos, CRGB endcolor );
+void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2);
+void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3);
+void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4);
+
+
+// fadeLightBy and fade_video - reduce the brightness of an array
+// of pixels all at once. Guaranteed
+// to never fade all the way to black.
+// (The two names are synonyms.)
+void fadeLightBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy);
+void fade_video( CRGB* leds, uint16_t num_leds, uint8_t fadeBy);
+
+// nscale8_video - scale down the brightness of an array of pixels
+// all at once. Guaranteed to never scale a pixel
+// all the way down to black, unless 'scale' is zero.
+void nscale8_video( CRGB* leds, uint16_t num_leds, uint8_t scale);
+
+// fadeToBlackBy and fade_raw - reduce the brightness of an array
+// of pixels all at once. These
+// functions will eventually fade all
+// the way to black.
+// (The two names are synonyms.)
+void fadeToBlackBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy);
+void fade_raw( CRGB* leds, uint16_t num_leds, uint8_t fadeBy);
+
+// nscale8 - scale down the brightness of an array of pixels
+// all at once. This function can scale pixels all the
+// way down to black even if 'scale' is not zero.
+void nscale8( CRGB* leds, uint16_t num_leds, uint8_t scale);
+
+
+
+// Pixel blending
+//
+// blend - computes a new color blended some fraction of the way
+// between two other colors.
+CRGB blend( const CRGB& p1, const CRGB& p2, fract8 amountOfP2 );
+
+CHSV blend( const CHSV& p1, const CHSV& p2, fract8 amountOfP2,
+ TGradientDirectionCode directionCode = SHORTEST_HUES );
+
+// blend - computes a new color blended array of colors, each
+// a given fraction of the way between corresponding
+// elements of two source arrays of colors.
+// Useful for blending palettes.
+CRGB* blend( const CRGB* src1, const CRGB* src2, CRGB* dest,
+ uint16_t count, fract8 amountOfsrc2 );
+
+CHSV* blend( const CHSV* src1, const CHSV* src2, CHSV* dest,
+ uint16_t count, fract8 amountOfsrc2,
+ TGradientDirectionCode directionCode = SHORTEST_HUES );
+
+// nblend - destructively modifies one color, blending
+// in a given fraction of an overlay color
+CRGB& nblend( CRGB& existing, const CRGB& overlay, fract8 amountOfOverlay );
+
+CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay,
+ TGradientDirectionCode directionCode = SHORTEST_HUES );
+
+// nblend - destructively blends a given fraction of
+// a new color array into an existing color array
+void nblend( CRGB* existing, CRGB* overlay, uint16_t count, fract8 amountOfOverlay);
+
+void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOverlay,
+ TGradientDirectionCode directionCode = SHORTEST_HUES);
+
+
+
+// CRGB HeatColor( uint8_t temperature)
+//
+// Approximates a 'black body radiation' spectrum for
+// a given 'heat' level. This is useful for animations of 'fire'.
+// Heat is specified as an arbitrary scale from 0 (cool) to 255 (hot).
+// This is NOT a chromatically correct 'black body radiation'
+// spectrum, but it's surprisingly close, and it's fast and small.
+CRGB HeatColor( uint8_t temperature);
+
+
+// Palettes
+//
+// RGB Palettes map an 8-bit value (0..255) to an RGB color.
+//
+// You can create any color palette you wish; a couple of starters
+// are provided: Forest, Clouds, Lava, Ocean, Rainbow, and Rainbow Stripes.
+//
+// Palettes come in the traditional 256-entry variety, which take
+// up 768 bytes of RAM, and lightweight 16-entry varieties. The 16-entry
+// variety automatically interpolates between its entries to produce
+// a full 256-element color map, but at a cost of only 48 bytes or RAM.
+//
+// Basic operation is like this: (example shows the 16-entry variety)
+// 1. Declare your palette storage:
+// CRGBPalette16 myPalette;
+//
+// 2. Fill myPalette with your own 16 colors, or with a preset color scheme.
+// You can specify your 16 colors a variety of ways:
+// CRGBPalette16 myPalette(
+// CRGB::Black,
+// CRGB::Black,
+// CRGB::Red,
+// CRGB::Yellow,
+// CRGB::Green,
+// CRGB::Blue,
+// CRGB::Purple,
+// CRGB::Black,
+//
+// 0x100000,
+// 0x200000,
+// 0x400000,
+// 0x800000,
+//
+// CHSV( 30,255,255),
+// CHSV( 50,255,255),
+// CHSV( 70,255,255),
+// CHSV( 90,255,255)
+// );
+//
+// Or you can initiaize your palette with a preset color scheme:
+// myPalette = RainbowStripesColors_p;
+//
+// 3. Any time you want to set a pixel to a color from your palette, use
+// "ColorFromPalette(...)" as shown:
+//
+// uint8_t index = /* any value 0..255 */;
+// leds[i] = ColorFromPalette( myPalette, index);
+//
+// Even though your palette has only 16 explicily defined entries, you
+// can use an 'index' from 0..255. The 16 explicit palette entries will
+// be spread evenly across the 0..255 range, and the intermedate values
+// will be RGB-interpolated between adjacent explicit entries.
+//
+// It's easier to use than it sounds.
+//
+
+class CRGBPalette16;
+class CRGBPalette256;
+class CHSVPalette16;
+class CHSVPalette256;
+typedef prog_uint32_t TProgmemRGBPalette16[16];
+typedef prog_uint32_t TProgmemHSVPalette16[16];
+#define TProgmemPalette16 TProgmemRGBPalette16
+
+// 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];
+ CHSVPalette16() {};
+ CHSVPalette16( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03,
+ const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07,
+ const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11,
+ const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 )
+ {
+ entries[0]=c00; entries[1]=c01; entries[2]=c02; entries[3]=c03;
+ entries[4]=c04; entries[5]=c05; entries[6]=c06; entries[7]=c07;
+ 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));
+ }
+ CHSVPalette16& operator=( const CHSVPalette16& rhs)
+ {
+ 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);
+ entries[i].hue = xyz.red;
+ entries[i].sat = xyz.green;
+ entries[i].val = xyz.blue;
+ }
+ }
+ CHSVPalette16& operator=( const TProgmemHSVPalette16& rhs)
+ {
+ for( uint8_t i = 0; i < 16; i++) {
+ CRGB xyz = 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];
+ }
+ inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline))
+ {
+ return entries[x];
+ }
+
+ inline CHSV& operator[] (int x) __attribute__((always_inline))
+ {
+ return entries[(uint8_t)x];
+ }
+ inline const CHSV& operator[] (int x) const __attribute__((always_inline))
+ {
+ return entries[(uint8_t)x];
+ }
+
+ operator CHSV*()
+ {
+ return &(entries[0]);
+ }
+
+ CHSVPalette16( const CHSV& c1)
+ {
+ fill_solid( &(entries[0]), 16, c1);
+ }
+ CHSVPalette16( const CHSV& c1, const CHSV& c2)
+ {
+ fill_gradient( &(entries[0]), 16, c1, c2);
+ }
+ CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3)
+ {
+ fill_gradient( &(entries[0]), 16, c1, c2, c3);
+ }
+ CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4)
+ {
+ fill_gradient( &(entries[0]), 16, c1, c2, c3, c4);
+ }
+
+};
+
+class CHSVPalette256 {
+public:
+ CHSV entries[256];
+ CHSVPalette256() {};
+ CHSVPalette256( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03,
+ const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07,
+ const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11,
+ const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 )
+ {
+ CHSVPalette16 p16(c00,c01,c02,c03,c04,c05,c06,c07,
+ c08,c09,c10,c11,c12,c13,c14,c15);
+ *this = p16;
+ };
+
+ CHSVPalette256( const CHSVPalette256& rhs)
+ {
+ memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
+ }
+ CHSVPalette256& operator=( const CHSVPalette256& rhs)
+ {
+ memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
+ return *this;
+ }
+
+ CHSVPalette256( const CHSVPalette16& rhs16)
+ {
+ UpscalePalette( rhs16, *this);
+ }
+ CHSVPalette256& operator=( const CHSVPalette16& rhs16)
+ {
+ UpscalePalette( rhs16, *this);
+ return *this;
+ }
+
+ CHSVPalette256( const TProgmemRGBPalette16& rhs)
+ {
+ CHSVPalette16 p16(rhs);
+ *this = p16;
+ }
+ CHSVPalette256& operator=( const TProgmemRGBPalette16& rhs)
+ {
+ CHSVPalette16 p16(rhs);
+ *this = p16;
+ return *this;
+ }
+
+ inline CHSV& operator[] (uint8_t x) __attribute__((always_inline))
+ {
+ return entries[x];
+ }
+ inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline))
+ {
+ return entries[x];
+ }
+
+ inline CHSV& operator[] (int x) __attribute__((always_inline))
+ {
+ return entries[(uint8_t)x];
+ }
+ inline const CHSV& operator[] (int x) const __attribute__((always_inline))
+ {
+ return entries[(uint8_t)x];
+ }
+
+ operator CHSV*()
+ {
+ return &(entries[0]);
+ }
+
+ CHSVPalette256( const CHSV& c1)
+ {
+ fill_solid( &(entries[0]), 256, c1);
+ }
+ CHSVPalette256( const CHSV& c1, const CHSV& c2)
+ {
+ fill_gradient( &(entries[0]), 256, c1, c2);
+ }
+ CHSVPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3)
+ {
+ fill_gradient( &(entries[0]), 256, c1, c2, c3);
+ }
+ CHSVPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4)
+ {
+ fill_gradient( &(entries[0]), 256, c1, c2, c3, c4);
+ }
+};
+
+class CRGBPalette16 {
+public:
+ CRGB entries[16];
+ CRGBPalette16() {};
+ CRGBPalette16( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03,
+ const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07,
+ const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11,
+ const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 )
+ {
+ entries[0]=c00; entries[1]=c01; entries[2]=c02; entries[3]=c03;
+ entries[4]=c04; entries[5]=c05; entries[6]=c06; entries[7]=c07;
+ 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));
+ }
+ CRGBPalette16& operator=( const CRGBPalette16& rhs)
+ {
+ 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++) {
+ entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion
+ }
+ return *this;
+ }
+
+ CRGBPalette16( const TProgmemRGBPalette16& rhs)
+ {
+ for( uint8_t i = 0; i < 16; i++) {
+ entries[i] = 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);
+ }
+ return *this;
+ }
+
+ inline CRGB& operator[] (uint8_t x) __attribute__((always_inline))
+ {
+ return entries[x];
+ }
+ inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline))
+ {
+ return entries[x];
+ }
+
+ inline CRGB& operator[] (int x) __attribute__((always_inline))
+ {
+ return entries[(uint8_t)x];
+ }
+ inline const CRGB& operator[] (int x) const __attribute__((always_inline))
+ {
+ return entries[(uint8_t)x];
+ }
+
+ operator CRGB*()
+ {
+ return &(entries[0]);
+ }
+
+ CRGBPalette16( const CHSV& c1)
+ {
+ fill_solid( &(entries[0]), 16, c1);
+ }
+ CRGBPalette16( const CHSV& c1, const CHSV& c2)
+ {
+ fill_gradient( &(entries[0]), 16, c1, c2);
+ }
+ CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3)
+ {
+ fill_gradient( &(entries[0]), 16, c1, c2, c3);
+ }
+ CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4)
+ {
+ fill_gradient( &(entries[0]), 16, c1, c2, c3, c4);
+ }
+
+ CRGBPalette16( const CRGB& c1)
+ {
+ fill_solid( &(entries[0]), 16, c1);
+ }
+ CRGBPalette16( const CRGB& c1, const CRGB& c2)
+ {
+ fill_gradient_RGB( &(entries[0]), 16, c1, c2);
+ }
+ CRGBPalette16( const CRGB& c1, const CRGB& c2, const CRGB& c3)
+ {
+ fill_gradient_RGB( &(entries[0]), 16, c1, c2, c3);
+ }
+ CRGBPalette16( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4)
+ {
+ fill_gradient_RGB( &(entries[0]), 16, c1, c2, c3, c4);
+ }
+
+
+};
+
+class CRGBPalette256 {
+public:
+ CRGB entries[256];
+ CRGBPalette256() {};
+ CRGBPalette256( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03,
+ const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07,
+ const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11,
+ const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 )
+ {
+ CRGBPalette16 p16(c00,c01,c02,c03,c04,c05,c06,c07,
+ c08,c09,c10,c11,c12,c13,c14,c15);
+ *this = p16;
+ };
+
+ CRGBPalette256( const CRGBPalette256& rhs)
+ {
+ memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
+ }
+ CRGBPalette256& operator=( const CRGBPalette256& rhs)
+ {
+ memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
+ return *this;
+ }
+
+ CRGBPalette256( const CHSVPalette256& rhs)
+ {
+ for( int i = 0; i < 256; i++) {
+ entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion
+ }
+ }
+ CRGBPalette256& operator=( const CHSVPalette256& rhs)
+ {
+ for( int i = 0; i < 256; i++) {
+ entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion
+ }
+ return *this;
+ }
+
+ CRGBPalette256( const CRGBPalette16& rhs16)
+ {
+ UpscalePalette( rhs16, *this);
+ }
+ CRGBPalette256& operator=( const CRGBPalette16& rhs16)
+ {
+ UpscalePalette( rhs16, *this);
+ return *this;
+ }
+
+ CRGBPalette256( const TProgmemRGBPalette16& rhs)
+ {
+ CRGBPalette16 p16(rhs);
+ *this = p16;
+ }
+ CRGBPalette256& operator=( const TProgmemRGBPalette16& rhs)
+ {
+ CRGBPalette16 p16(rhs);
+ *this = p16;
+ return *this;
+ }
+
+ inline CRGB& operator[] (uint8_t x) __attribute__((always_inline))
+ {
+ return entries[x];
+ }
+ inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline))
+ {
+ return entries[x];
+ }
+
+ inline CRGB& operator[] (int x) __attribute__((always_inline))
+ {
+ return entries[(uint8_t)x];
+ }
+ inline const CRGB& operator[] (int x) const __attribute__((always_inline))
+ {
+ return entries[(uint8_t)x];
+ }
+
+ operator CRGB*()
+ {
+ return &(entries[0]);
+ }
+
+ CRGBPalette256( const CHSV& c1)
+ {
+ fill_solid( &(entries[0]), 256, c1);
+ }
+ CRGBPalette256( const CHSV& c1, const CHSV& c2)
+ {
+ fill_gradient( &(entries[0]), 256, c1, c2);
+ }
+ CRGBPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3)
+ {
+ fill_gradient( &(entries[0]), 256, c1, c2, c3);
+ }
+ CRGBPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4)
+ {
+ fill_gradient( &(entries[0]), 256, c1, c2, c3, c4);
+ }
+
+ CRGBPalette256( const CRGB& c1)
+ {
+ fill_solid( &(entries[0]), 256, c1);
+ }
+ CRGBPalette256( const CRGB& c1, const CRGB& c2)
+ {
+ fill_gradient_RGB( &(entries[0]), 256, c1, c2);
+ }
+ CRGBPalette256( const CRGB& c1, const CRGB& c2, const CRGB& c3)
+ {
+ fill_gradient_RGB( &(entries[0]), 256, c1, c2, c3);
+ }
+ CRGBPalette256( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4)
+ {
+ fill_gradient_RGB( &(entries[0]), 256, c1, c2, c3, c4);
+ }
+
+};
+
+
+
+
+typedef enum { NOBLEND=0, BLEND=1 } TBlendType;
+
+CRGB ColorFromPalette( const CRGBPalette16& pal,
+ uint8_t index,
+ uint8_t brightness=255,
+ TBlendType blendType=BLEND);
+
+CRGB ColorFromPalette( const CRGBPalette256& pal,
+ uint8_t index,
+ uint8_t brightness=255,
+ TBlendType blendType=NOBLEND );
+
+CHSV ColorFromPalette( const CHSVPalette16& pal,
+ uint8_t index,
+ uint8_t brightness=255,
+ TBlendType blendType=BLEND);
+
+CHSV ColorFromPalette( const CHSVPalette256& pal,
+ uint8_t index,
+ uint8_t brightness=255,
+ TBlendType blendType=NOBLEND );
+
+
+// Fill a range of LEDs with a sequece of entryies from a 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)
+{
+ uint8_t colorIndex = startIndex;
+ for( uint16_t i = 0; i < N; i++) {
+ L[i] = ColorFromPalette( pal, colorIndex, brightness, blendType);
+ colorIndex += 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)
+{
+ for( uint16_t i = 0; i < dataCount; i++) {
+ uint8_t d = dataArray[i];
+ CRGB rgb = ColorFromPalette( pal, d, brightness, blendType);
+ if( opacity == 255 ) {
+ targetColorArray[i] = rgb;
+ } else {
+ targetColorArray[i].nscale8( 256 - opacity);
+ rgb.nscale8_video( opacity);
+ targetColorArray[i] += rgb;
+ }
+ }
+}
+
+#endif
diff --git a/controller.h b/controller.h
index 9df96efa..a2519e1f 100644
--- a/controller.h
+++ b/controller.h
@@ -1,57 +1,335 @@
#ifndef __INC_CONTROLLER_H
#define __INC_CONTROLLER_H
-#include <avr/io.h>
+#include "led_sysdefs.h"
#include "pixeltypes.h"
+#include "color.h"
+#define RO(X) RGB_BYTE(RGB_ORDER, X)
+#define RGB_BYTE(RO,X) (((RO)>>(3*(2-(X)))) & 0x3)
-#define RGB_BYTE0(X) ((X>>6) & 0x3)
-#define RGB_BYTE1(X) ((X>>3) & 0x3)
-#define RGB_BYTE2(X) ((X) & 0x3)
+#define RGB_BYTE0(RO) ((RO>>6) & 0x3)
+#define RGB_BYTE1(RO) ((RO>>3) & 0x3)
+#define RGB_BYTE2(RO) ((RO) & 0x3)
// operator byte *(struct CRGB[] arr) { return (byte*)arr; }
+#define DISABLE_DITHER 0x00
+#define BINARY_DITHER 0x01
+typedef uint8_t EDitherMode;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
+//
// LED Controller interface definition
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/// Base definition for an LED controller. Pretty much the methods that every LED controller object will make available.
+/// Base definition for an LED controller. Pretty much the methods that every LED controller object will make available.
/// Note that the showARGB method is not impelemented for all controllers yet. Note also the methods for eventual checking
/// of background writing of data (I'm looking at you, teensy 3.0 DMA controller!). If you want to pass LED controllers around
/// to methods, make them references to this type, keeps your code saner. However, most people won't be seeing/using these objects
/// directly at all
-class CLEDController {
+class CLEDController {
+protected:
+ friend class CFastLED;
+ CRGB *m_Data;
+ CLEDController *m_pNext;
+ CRGB m_ColorCorrection;
+ CRGB m_ColorTemperature;
+ EDitherMode m_DitherMode;
+ int m_nLeds;
+ static CLEDController *m_pHead;
+ static CLEDController *m_pTail;
+
+ // set all the leds on the controller to a given 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
+ virtual void show(const struct CRGB *data, int nLeds, CRGB scale) = 0;
+
+#ifdef SUPPORT_ARGB
+ // as above, but every 4th uint8_t is assumed to be alpha channel data, and will be skipped
+ virtual void show(const struct CARGB *data, int nLeds, CRGB scale) = 0;
+#endif
public:
+ 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; }
+ if(m_pTail != NULL) { m_pTail->m_pNext = this; }
+ m_pTail = this;
+ }
+
// initialize the LED controller
virtual void init() = 0;
- // reset any internal state to a clean point
- virtual void reset() { init(); }
-
// clear out/zero out the given number of leds.
virtual void clearLeds(int 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) = 0;
+ // 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
+ 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
+ void showLeds(uint8_t brightness=255) {
+ show(m_Data, m_nLeds, getAdjustment(brightness));
+ }
- // 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
- virtual void show(const struct CRGB *data, int nLeds, uint8_t scale = 255) = 0;
+ void showColor(const struct CRGB & data, uint8_t brightness=255) {
+ showColor(data, m_nLeds, getAdjustment(brightness));
+ }
+
+ // navigating the list of controllers
+ static CLEDController *head() { return m_pHead; }
+ CLEDController *next() { return m_pNext; }
+
+ #ifdef SUPPORT_ARGB
+ // as above, but every 4th uint8_t is assumed to be alpha channel data, and will be skipped
+ void show(const struct CARGB *data, int nLeds, uint8_t brightness) {
+ show(data, nLeds, getAdjustment(brightness))
+ }
+#endif
+
+ CLEDController & setLeds(CRGB *data, int nLeds) {
+ m_Data = data;
+ m_nLeds = nLeds;
+ return *this;
+ }
+
+ void clearLedData() {
+ if(m_Data) {
+ memset8((void*)m_Data, 0, sizeof(struct CRGB) * m_nLeds);
+ }
+ }
+
+ // How many leds does this controller manage?
+ int size() { return m_nLeds; }
+
+ // Pointer to the CRGB array for this controller
+ CRGB* leds() { return m_Data; }
+
+ // Reference to the n'th item in the controller
+ CRGB &operator[](int x) { return m_Data[x]; }
+
+ inline CLEDController & setDither(uint8_t ditherMode = BINARY_DITHER) { m_DitherMode = ditherMode; return *this; }
+ inline uint8_t getDither() { return m_DitherMode; }
+
+ CLEDController & setCorrection(CRGB correction) { m_ColorCorrection = correction; return *this; }
+ CLEDController & setCorrection(LEDColorCorrection correction) { m_ColorCorrection = correction; return *this; }
+ CRGB getCorrection() { return m_ColorCorrection; }
+
+ CLEDController & setTemperature(CRGB temperature) { m_ColorTemperature = temperature; return *this; }
+ CLEDController & setTemperature(ColorTemperature temperature) { m_ColorTemperature = temperature; return *this; }
+ CRGB getTemperature() { return m_ColorTemperature; }
+
+ 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 adj;
+#endif
+ }
+};
+
+// 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;
+ int mLen;
+ uint8_t d[3];
+ uint8_t e[3];
+ CRGB mScale;
+ uint8_t mAdvance;
+
+ PixelController(const PixelController & 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;
+ }
+
+ 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;
+ mAdvance = (advance) ? 3+skip : 0;
+ }
+
+ PixelController(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;
+ }
+
+ PixelController(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;
+ }
#ifdef SUPPORT_ARGB
- // as above, but every 4th uint8_t is assumed to be alpha channel data, and will be skipped
- virtual void show(const struct CARGB *data, int nLeds, uint8_t scale = 255) = 0;
+ PixelController(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;
+ }
+
+ PixelController(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;
+ }
+#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
-
- // is the controller ready to write data out
- virtual bool ready() { return true; }
+ }
+
+ // 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;
+ }
+ }
- // wait until the controller is ready to write data out
- virtual void wait() { return; }
+ // 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(PixelController & pc) { return pc.mData[RO(SLOT)]; }
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t dither(PixelController & pc, uint8_t b) { return b ? qadd8(b, pc.d[RO(SLOT)]) : 0; }
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t scale(PixelController & pc, uint8_t b) { return scale8(b, pc.mScale.raw[RO(SLOT)]); }
+
+ // composite shortcut functions for loading, dithering, and scaling
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc) { return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc))); }
+ template<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc); }
+
+ // Helper functions to get around gcc stupidities
+ __attribute__((always_inline)) inline uint8_t loadAndScale0() { return loadAndScale<0>(*this); }
+ __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); }
};
-#endif \ No newline at end of file
+#endif
diff --git a/delay.h b/delay.h
index 2b1deaed..05313127 100644
--- a/delay.h
+++ b/delay.h
@@ -1,6 +1,24 @@
#ifndef __INC_DELAY_H
#define __INC_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
+template<int WAIT> class CMinWait {
+ uint16_t mLastMicros;
+public:
+ CMinWait() { mLastMicros = 0; }
+
+ void wait() {
+ uint16_t diff;
+ do {
+ diff = (micros() & 0xFFFF) - mLastMicros;
+ } while(diff < WAIT);
+ }
+
+ void mark() { mLastMicros = micros() & 0xFFFF; }
+};
+
+
////////////////////////////////////////////////////////////////////////////////////////////
//
// Clock cycle counted delay loop
@@ -19,6 +37,9 @@
template<int CYCLES> inline void delaycycles();
// TODO: ARM version of _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() {
delaycycles<PAD>();
@@ -35,13 +56,28 @@ template<int LOOP, int PAD> inline void _delaycycles_AVR() {
);
}
-// usable definition
-#if !defined(__MK20DX128__)
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() {
+// 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__ (
+// " mov.w r9, %0\n"
+// "L_%=: subs.w r9, r9, #1\n"
+// " bne.n L_%=\n"
+// : /* no outputs */
+// : "M" (LOOP)
+// : "r9"
+// );
+// }
+
+
template<int CYCLES> __attribute__((always_inline)) inline void delaycycles() {
+ // _delaycycles_ARM<CYCLES / 3, CYCLES % 3>();
NOP; delaycycles<CYCLES-1>();
}
#endif
@@ -65,7 +101,7 @@ template<> __attribute__((always_inline)) inline void delaycycles<5>() {NOP2;NOP
// Macro to convert from nano-seconds to clocks and clocks to nano-seconds
// #define NS(_NS) (_NS / (1000 / (F_CPU / 1000000L)))
-#if F_CPU < 96000000
+#if 1 || (F_CPU < 96000000)
#define NS(_NS) ( (_NS * (F_CPU / 1000000L))) / 1000
#define CLKS_TO_MICROS(_CLKS) ((long)(_CLKS)) / (F_CPU / 1000000L)
#else
@@ -76,9 +112,13 @@ template<> __attribute__((always_inline)) inline void delaycycles<5>() {NOP2;NOP
// 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(__MK20DX128__)
+
+#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;
@@ -89,4 +129,31 @@ template<> __attribute__((always_inline)) inline void delaycycles<5>() {NOP2;NOP
# endif
#endif
-#endif \ No newline at end of file
+#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
+
+#endif
diff --git a/dmx.h b/dmx.h
index 03329d0e..8892302f 100644
--- a/dmx.h
+++ b/dmx.h
@@ -1,45 +1,44 @@
#ifndef __INC_DMX_H
#define __INC_DMX_H
-//#ifdef DmxSimple_H
-//#if USE_DMX_SIMPLE
-#ifdef FASTSPI_USE_DMX_SIMPLE
+
+#ifdef DmxSimple_h
#include<DmxSimple.h>
+#define HAS_DMX_SIMPLE
+
// note - dmx simple must be included before FastSPI for this code to be enabled
-template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class DMXController : public CLEDController {
+template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class DMXSimpleController : public CLEDController {
public:
// initialize the LED controller
virtual void init() { DmxSimple.usePin(DATA_PIN); }
- // reset any internal state to a clean point
- virtual void reset() { init(); }
-
// clear out/zero out the given number of leds.
virtual void clearLeds(int nLeds) {
int count = min(nLeds * 3, DMX_SIZE);
for(int iChannel = 1; iChannel <= count; iChannel++) { DmxSimple.write(iChannel, 0); }
}
+protected:
// set all the leds on the controller to a given color
- virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
+ virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) {
int count = min(nLeds, DMX_SIZE / 3);
int iChannel = 1;
for(int i = 0; i < count; i++) {
- DmxSimple.write(iChannel++, scale8(data[RGB_BYTE0(RGB_ORDER)], scale));
- DmxSimple.write(iChannel++, scale8(data[RGB_BYTE1(RGB_ORDER)], scale));
- DmxSimple.write(iChannel++, scale8(data[RGB_BYTE2(RGB_ORDER)], scale));
+ DmxSimple.write(iChannel++, scale8(data[RGB_BYTE0(RGB_ORDER)], scale.raw[RGB_BYTE0(RGB_ORDER)]));
+ DmxSimple.write(iChannel++, scale8(data[RGB_BYTE1(RGB_ORDER)], scale.raw[RGB_BYTE1(RGB_ORDER)]));
+ DmxSimple.write(iChannel++, scale8(data[RGB_BYTE2(RGB_ORDER)], scale.raw[RGB_BYTE2(RGB_ORDER)]));
}
}
- // note that the uint8_ts will be in the order that you want them sent out to the device.
+ // 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
- virtual void show(const struct CRGB *data, int nLeds, uint8_t scale = 255) {
+ virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
int count = min(nLeds, DMX_SIZE / 3);
int iChannel = 1;
for(int i = 0; i < count; i++) {
- DmxSimple.write(iChannel++, scale8(data[i][RGB_BYTE0(RGB_ORDER)], scale));
- DmxSimple.write(iChannel++, scale8(data[i][RGB_BYTE1(RGB_ORDER)], scale));
- DmxSimple.write(iChannel++, scale8(data[i][RGB_BYTE2(RGB_ORDER)], scale));
+ DmxSimple.write(iChannel++, scale8(data[i][RGB_BYTE0(RGB_ORDER)], scale.raw[RGB_BYTE0(RGB_ORDER)]));
+ DmxSimple.write(iChannel++, scale8(data[i][RGB_BYTE1(RGB_ORDER)], scale.raw[RGB_BYTE1(RGB_ORDER)]));
+ DmxSimple.write(iChannel++, scale8(data[i][RGB_BYTE2(RGB_ORDER)], scale.raw[RGB_BYTE2(RGB_ORDER)]));
}
}
@@ -48,51 +47,44 @@ public:
// as above, but every 4th uint8_t is assumed to be alpha channel data, and will be skipped
virtual void show(const struct CARGB *data, int nLeds, uint8_t scale = 255) = 0;
#endif
-
- // is the controller ready to write data out
- virtual bool ready() { return true; }
-
- // wait until the controller is ready to write data out
- virtual void wait() { return; }
-
};
-#elif defined(FASTSPI_USE_DMX_SERIAL)
+#endif
+
+#ifdef DmxSerial_h
+#include<DMXSerial.h>
-template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class DMXController : public CLEDController {
+template <EOrder RGB_ORDER = RGB> class DMXSerialController : public CLEDController {
public:
// initialize the LED controller
virtual void init() { DMXSerial.init(DMXController); }
- // reset any internal state to a clean point
- virtual void reset() { init(); }
-
// clear out/zero out the given number of leds.
virtual void clearLeds(int nLeds) {
- int count = min(nLeds * 3, DMX_SIZE);
- for(int iChannel = 0; iChannel < count; iChannel++) { DmxSimple.write(iChannel, 0); }
+ int count = min(nLeds * 3, DMXSERIAL_MAX);
+ for(int iChannel = 0; iChannel < count; iChannel++) { DMXSerial.write(iChannel, 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) {
- int count = min(nLeds, DMX_SIZE / 3);
+ virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) {
+ int count = min(nLeds, DMXSERIAL_MAX / 3);
int iChannel = 0;
for(int i = 0; i < count; i++) {
- DMXSerial.write(iChannel++, scale8(data[RGB_BYTE0(RGB_ORDER)], scale));
- DMXSerial.write(iChannel++, scale8(data[RGB_BYTE1(RGB_ORDER)], scale));
- DMXSerial.write(iChannel++, scale8(data[RGB_BYTE2(RGB_ORDER)], scale));
+ DMXSerial.write(iChannel++, scale8(data[RGB_BYTE0(RGB_ORDER)], scale.raw[RGB_BYTE0(RGB_ORDER)]));
+ DMXSerial.write(iChannel++, scale8(data[RGB_BYTE1(RGB_ORDER)], scale.raw[RGB_BYTE1(RGB_ORDER)]));
+ DMXSerial.write(iChannel++, scale8(data[RGB_BYTE2(RGB_ORDER)], scale.raw[RGB_BYTE2(RGB_ORDER)]));
}
}
- // note that the uint8_ts will be in the order that you want them sent out to the device.
+ // 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
- virtual void show(const struct CRGB *data, int nLeds, uint8_t scale = 255) {
- int count = min(nLeds, DMX_SIZE / 3);
+ virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
+ int count = min(nLeds, DMXSERIAL_MAX / 3);
int iChannel = 0;
for(int i = 0; i < count; i++) {
- DMXSerial.write(iChannel++, scale8(data[i][RGB_BYTE0(RGB_ORDER)], scale));
- DMXSerial.write(iChannel++, scale8(data[i][RGB_BYTE1(RGB_ORDER)], scale));
- DMXSerial.write(iChannel++, scale8(data[i][RGB_BYTE2(RGB_ORDER)], scale));
+ DMXSerial.write(iChannel++, scale8(data[i][RGB_BYTE0(RGB_ORDER)], scale.raw[RGB_BYTE0(RGB_ORDER)]));
+ DMXSerial.write(iChannel++, scale8(data[i][RGB_BYTE1(RGB_ORDER)], scale.raw[RGB_BYTE1(RGB_ORDER)]));
+ DMXSerial.write(iChannel++, scale8(data[i][RGB_BYTE2(RGB_ORDER)], scale.raw[RGB_BYTE2(RGB_ORDER)]));
}
}
@@ -101,15 +93,8 @@ public:
// as above, but every 4th uint8_t is assumed to be alpha channel data, and will be skipped
virtual void show(const struct CARGB *data, int nLeds, uint8_t scale = 255) = 0;
#endif
-
- // is the controller ready to write data out
- virtual bool ready() { return true; }
-
- // wait until the controller is ready to write data out
- virtual void wait() { return; }
-
};
-
+#define HAS_DMX_SERIAL
#endif
-#endif \ No newline at end of file
+#endif
diff --git a/examples/AnalogOutput/AnalogOutput.ino b/examples/AnalogOutput/AnalogOutput.ino
new file mode 100644
index 00000000..33733102
--- /dev/null
+++ b/examples/AnalogOutput/AnalogOutput.ino
@@ -0,0 +1,65 @@
+#include <FastLED.h>
+
+// Example showing how to use FastLED color functions
+// even when you're NOT using a "pixel-addressible" smart LED strip.
+//
+// This example is designed to control an "analog" RGB LED strip
+// (or a single RGB LED) being driven by Arduino PWM output pins.
+// So this code never calls FastLED.addLEDs() or FastLED.show().
+//
+// This example illustrates one way you can use just the portions
+// of FastLED that you need. In this case, this code uses just the
+// fast HSV color conversion code.
+//
+// In this example, the RGB values are output on three separate
+// 'analog' PWM pins, one for red, one for green, and one for blue.
+
+#define REDPIN 5
+#define GREENPIN 6
+#define BLUEPIN 3
+
+// showAnalogRGB: this is like FastLED.show(), but outputs on
+// analog PWM output pins instead of sending data to an intelligent,
+// pixel-addressable LED strip.
+//
+// This function takes the incoming RGB values and outputs the values
+// on three analog PWM output pins to the r, g, and b values respectively.
+void showAnalogRGB( const CRGB& rgb)
+{
+ analogWrite(REDPIN, rgb.r );
+ analogWrite(GREENPIN, rgb.g );
+ analogWrite(BLUEPIN, rgb.b );
+}
+
+
+
+// colorBars: flashes Red, then Green, then Blue, then Black.
+// Helpful for diagnosing if you've mis-wired which is which.
+void colorBars()
+{
+ showAnalogRGB( CRGB::Red ); delay(500);
+ showAnalogRGB( CRGB::Green ); delay(500);
+ showAnalogRGB( CRGB::Blue ); delay(500);
+ showAnalogRGB( CRGB::Black ); delay(500);
+}
+
+void loop()
+{
+ static uint8_t hue;
+ hue = hue + 1;
+ // Use FastLED automatic HSV->RGB conversion
+ showAnalogRGB( CHSV( hue, 255, 255) );
+
+ delay(20);
+}
+
+
+void setup() {
+ pinMode(REDPIN, OUTPUT);
+ pinMode(GREENPIN, OUTPUT);
+ pinMode(BLUEPIN, OUTPUT);
+
+ // Flash the "hello" color sequence: R, G, B, black.
+ colorBars();
+}
+
diff --git a/examples/Blink/Blink.ino b/examples/Blink/Blink.ino
index bc5c84d2..ab307fda 100644
--- a/examples/Blink/Blink.ino
+++ b/examples/Blink/Blink.ino
@@ -1,7 +1,7 @@
#include "FastLED.h"
// How many leds in your strip?
-#define NUM_LEDS 6
+#define NUM_LEDS 1
// 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,
@@ -20,9 +20,12 @@ void setup() {
// FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);
- FastLED.addLeds<NEOPIXEL, DATA_PIN, RGB>(leds, NUM_LEDS);
+ FastLED.addLeds<NEOPIXEL, DATA_PIN>(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);
+ // FastLED.addLeds<GW6205_400, DATA_PIN, RGB>(leds, NUM_LEDS);
+
// FastLED.addLeds<WS2801, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SM16716, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<LPD8806, RGB>(leds, NUM_LEDS);
diff --git a/examples/ColorPalette/ColorPalette.ino b/examples/ColorPalette/ColorPalette.ino
new file mode 100644
index 00000000..93318998
--- /dev/null
+++ b/examples/ColorPalette/ColorPalette.ino
@@ -0,0 +1,189 @@
+#include <FastLED.h>
+
+#define LED_PIN 5
+#define NUM_LEDS 50
+#define BRIGHTNESS 64
+#define LED_TYPE WS2811
+#define COLOR_ORDER GRB
+CRGB leds[NUM_LEDS];
+
+#define UPDATES_PER_SECOND 100
+
+// 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
+// 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
+// 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
+// FastLED compact palettes are at the bottom of this file.
+
+
+
+CRGBPalette16 currentPalette;
+TBlendType currentBlending;
+
+extern CRGBPalette16 myRedWhiteBluePalette;
+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;
+}
+
+
+void loop()
+{
+ 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;
+ }
+}
+
+
+// There are several different palettes of colors demonstrated here.
+//
+// FastLED provides several 'preset' palettes: RainbowColors_p, RainbowStripeColors_p,
+// OceanColors_p, CloudColors_p, LavaColors_p, ForestColors_p, and PartyColors_p.
+//
+// Additionally, you can manually define your own color palettes, or you can write
+// code that creates color palettes on the fly. All are shown here.
+
+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; }
+ }
+}
+
+// This function fills the palette with totally random colors.
+void SetupTotallyRandomPalette()
+{
+ for( int i = 0; i < 16; i++) {
+ currentPalette[i] = CHSV( random8(), 255, random8());
+ }
+}
+
+// This function sets up a palette of black and white stripes,
+// using code. Since the palette is effectively an array of
+// sixteen CRGB colors, the various fill_* functions can be used
+// 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;
+
+}
+
+// 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 );
+}
+
+
+// This example shows how to set up a static color palette
+// 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
+};
+
+
+
+// Additionl notes on FastLED compact palettes:
+//
+// Normally, in computer graphics, the palette (or "color lookup table")
+// 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.
+//
+// FastLED does offer traditional 256-element palettes, for setups that
+// can afford the 768-byte cost in RAM.
+//
+// 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
+// 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/ColorTemperature/ColorTemperature.ino b/examples/ColorTemperature/ColorTemperature.ino
new file mode 100644
index 00000000..66093e06
--- /dev/null
+++ b/examples/ColorTemperature/ColorTemperature.ino
@@ -0,0 +1,85 @@
+#include <FastLED.h>
+
+#define LED_PIN 3
+
+// Information about the LED strip itself
+#define NUM_LEDS 60
+#define CHIPSET WS2811
+#define COLOR_ORDER GRB
+CRGB leds[NUM_LEDS];
+
+#define BRIGHTNESS 128
+
+
+// FastLED v2.1 provides two color-management controls:
+// (1) color correction settings for each LED strip, and
+// (2) master control of the overall output 'color temperature'
+//
+// THIS EXAMPLE demonstrates the second, "color temperature" control.
+// It shows a simple rainbow animation first with one temperature profile,
+// and a few seconds later, with a different temperature profile.
+//
+// The first pixel of the strip will show the color temperature.
+//
+// HELPFUL HINTS for "seeing" the effect in this demo:
+// * Don't look directly at the LED pixels. Shine the LEDs aganst
+// a white wall, table, or piece of paper, and look at the reflected light.
+//
+// * If you watch it for a bit, and then walk away, and then come back
+// to it, you'll probably be able to "see" whether it's currently using
+// the 'redder' or the 'bluer' temperature profile, even not counting
+// the lowest 'indicator' pixel.
+//
+//
+// FastLED provides these pre-conigured incandescent color profiles:
+// Candle, Tungsten40W, Tungsten100W, Halogen, CarbonArc,
+// HighNoonSun, DirectSunlight, OvercastSky, ClearBlueSky,
+// FastLED provides these pre-configured gaseous-light color profiles:
+// WarmFluorescent, StandardFluorescent, CoolWhiteFluorescent,
+// FullSpectrumFluorescent, GrowLightFluorescent, BlackLightFluorescent,
+// MercuryVapor, SodiumVapor, MetalHalide, HighPressureSodium,
+// FastLED also provides an "Uncorrected temperature" profile
+// UncorrectedTemperature;
+
+#define TEMPERATURE_1 Tungsten100W
+#define TEMPERATURE_2 OvercastSky
+
+// How many seconds to show each temperature before switching
+#define DISPLAYTIME 20
+// How many seconds to show black between switches
+#define BLACKTIME 3
+
+void loop()
+{
+ // draw a generic, no-name rainbow
+ static uint8_t starthue = 0;
+ fill_rainbow( leds + 5, NUM_LEDS - 5, --starthue, 20);
+
+ // Choose which 'color temperature' profile to enable.
+ uint8_t secs = (millis() / 1000) % (DISPLAYTIME * 2);
+ if( secs < DISPLAYTIME) {
+ FastLED.setTemperature( TEMPERATURE_1 ); // first temperature
+ leds[0] = TEMPERATURE_1; // show indicator pixel
+ } else {
+ FastLED.setTemperature( TEMPERATURE_2 ); // second temperature
+ leds[0] = TEMPERATURE_2; // show indicator pixel
+ }
+
+ // Black out the LEDs for a few secnds between color changes
+ // to let the eyes and brains adjust
+ if( (secs % DISPLAYTIME) < BLACKTIME) {
+ memset8( leds, 0, NUM_LEDS * sizeof(CRGB));
+ }
+
+ FastLED.show();
+ FastLED.delay(8);
+}
+
+void setup() {
+ delay( 3000 ); // power-up safety delay
+ // It's important to set the color correction for your LED strip here,
+ // so that colors can be more accurately rendered through the 'temperature' profiles
+ FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalSMD5050 );
+ FastLED.setBrightness( BRIGHTNESS );
+}
+
diff --git a/examples/Cylon/Cylon.ino b/examples/Cylon/Cylon.ino
index bec878c5..dfb6ed78 100644
--- a/examples/Cylon/Cylon.ino
+++ b/examples/Cylon/Cylon.ino
@@ -1,19 +1,19 @@
#include "FastLED.h"
// How many leds in your strip?
-#define NUM_LEDS 6
+#define NUM_LEDS 40
// 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 11
+#define DATA_PIN 6
#define CLOCK_PIN 13
// Define the array of leds
CRGB leds[NUM_LEDS];
void setup() {
- FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
+ FastLED.addLeds<NEOPIXEL,DATA_PIN>(leds, NUM_LEDS);
}
void loop() {
@@ -40,4 +40,4 @@ void loop() {
// Wait a little bit before we loop around and do it again
delay(30);
}
-} \ No newline at end of file
+}
diff --git a/examples/Fast2Dev/Fast2Dev.ino b/examples/Fast2Dev/Fast2Dev.ino
deleted file mode 100644
index 16aa2544..00000000
--- a/examples/Fast2Dev/Fast2Dev.ino
+++ /dev/null
@@ -1,99 +0,0 @@
-// Uncomment this line if you have any interrupts that are changing pins - this causes the library to be a little bit more cautious
-// #define FAST_SPI_INTERRUPTS_WRITE_PINS 1
-
-// Uncomment this line to force always using software, instead of hardware, SPI (why?)
-// #define FORCE_SOFTWARE_SPI 1
-
-// Uncomment this line if you want to talk to DMX controllers
-// #define FASTSPI_USE_DMX_SIMPLE 1
-
-#include "FastLED.h"
-
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// test code
-//
-//////////////////////////////////////////////////
-
-#define NUM_LEDS 150
-
-CRGB leds[NUM_LEDS];
-
-void setup() {
- // sanity check delay - allows reprogramming if accidently blowing power w/leds
- delay(2000);
-
- // For safety (to prevent too high of a power draw), the test case defaults to
- // setting brightness to 25% brightness
- LEDS.setBrightness(64);
-
- // LEDS.addLeds<WS2811, 13>(leds, NUM_LEDS);
- // LEDS.addLeds<TM1809, 13>(leds, NUM_LEDS);
- // LEDS.addLeds<UCS1903, 13>(leds, NUM_LEDS);
- // LEDS.addLeds<TM1803, 13>(leds, NUM_LEDS);
-
- // LEDS.addLeds<P9813>(leds, NUM_LEDS);
- CLEDController *pLed = new ClocklessController_Trinket<4, NS(400), NS(400), NS(450), GRB>();
- LEDS.addLeds(pLed, leds, NUM_LEDS);
- // LEDS.addLeds<LPD8806>(leds, NUM_LEDS);
- // LEDS.addLeds<WS2801>(leds, NUM_LEDS);
- // LEDS.addLeds<SM16716>(leds, NUM_LEDS);
-
- // LEDS.addLeds<WS2811, 11>(leds, NUM_LEDS);
-
- // Put ws2801 strip on the hardware SPI pins with a BGR ordering of rgb and limited to a 1Mhz data rate
- // LEDS.addLeds<WS2801, 11, 13, BGR, DATA_RATE_MHZ(1)>(leds, NUM_LEDS);
-
- // LEDS.addLeds<LPD8806, 10, 11>(leds, NUM_LEDS);
- // LEDS.addLeds<WS2811, 13, BRG>(leds, NUM_LEDS);
- // LEDS.addLeds<LPD8806, BGR>(leds, NUM_LEDS);
-}
-
-void loop() {
- for(int i = 0; i < 3; i++) {
- for(int iLed = 0; iLed < NUM_LEDS; iLed++) {
- memset(leds, 0, NUM_LEDS * sizeof(struct CRGB));
-
- switch(i) {
- // You can access the rgb values by field r, g, b
- case 0: leds[iLed].r = 128; break;
-
- // or by indexing into the led (r==0, g==1, b==2)
- case 1: leds[iLed][i] = 128; break;
-
- // or by setting the rgb values for the pixel all at once
- case 2: leds[iLed] = CRGB(0, 0, 128); break;
- }
-
- // and now, show your led array!
- LEDS.show();
- delay(10);
- }
-
- // fade up
- for(int x = 0; x < 128; x++) {
- // The showColor method sets all the leds in the strip to the same color
- LEDS.showColor(CRGB(x, 0, 0));
- delay(10);
- }
-
- // fade down
- for(int x = 128; x >= 0; x--) {
- LEDS.showColor(CRGB(x, 0, 0));
- delay(10);
- }
-
- // let's fade up by scaling the brightness
- for(int scale = 0; scale < 128; scale++) {
- LEDS.showColor(CRGB(0, 128, 0), scale);
- delay(10);
- }
-
- // let's fade down by scaling the brightness
- for(int scale = 128; scale > 0; scale--) {
- LEDS.showColor(CRGB(0, 128, 0), scale);
- delay(10);
- }
- }
-}
diff --git a/examples/Fire2012/Fire2012.ino b/examples/Fire2012/Fire2012.ino
new file mode 100644
index 00000000..0929e2f2
--- /dev/null
+++ b/examples/Fire2012/Fire2012.ino
@@ -0,0 +1,96 @@
+#include <FastLED.h>
+
+#define LED_PIN 5
+#define COLOR_ORDER GRB
+#define CHIPSET WS2811
+#define NUM_LEDS 30
+
+#define BRIGHTNESS 200
+#define FRAMES_PER_SECOND 60
+
+CRGB leds[NUM_LEDS];
+
+void setup() {
+ delay(3000); // sanity delay
+ FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
+ FastLED.setBrightness( BRIGHTNESS );
+}
+
+void loop()
+{
+ // Add entropy to random number generator; we use a lot of it.
+ random16_add_entropy( random());
+
+ Fire2012(); // run simulation frame
+
+ FastLED.show(); // display this frame
+ FastLED.delay(1000 / FRAMES_PER_SECOND);
+}
+
+
+// Fire2012 by Mark Kriegsman, July 2012
+// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY
+////
+// This basic one-dimensional 'fire' simulation works roughly as follows:
+// There's a underlying array of 'heat' cells, that model the temperature
+// at each point along the line. Every cycle through the simulation,
+// four steps are performed:
+// 1) All cells cool down a little bit, losing heat to the air
+// 2) The heat from each cell drifts 'up' and diffuses a little
+// 3) Sometimes randomly new 'sparks' of heat are added at the bottom
+// 4) The heat from each cell is rendered as a color into the leds array
+// The heat-to-color mapping uses a black-body radiation approximation.
+//
+// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
+//
+// This simulation scales it self a bit depending on NUM_LEDS; it should look
+// "OK" on anywhere from 20 to 100 LEDs without too much tweaking.
+//
+// I recommend running this simulation at anywhere from 30-100 frames per second,
+// meaning an interframe delay of about 10-35 milliseconds.
+//
+// Looks best on a high-density LED setup (60+ pixels/meter).
+//
+//
+// There are two main parameters you can play with to control the look and
+// feel of your fire: COOLING (used in step 1 above), and SPARKING (used
+// in step 3 above).
+//
+// COOLING: How much does the air cool as it rises?
+// Less cooling = taller flames. More cooling = shorter flames.
+// Default 50, suggested range 20-100
+#define COOLING 55
+
+// SPARKING: What chance (out of 255) is there that a new spark will be lit?
+// Higher chance = more roaring fire. Lower chance = more flickery fire.
+// Default 120, suggested range 50-200.
+#define SPARKING 120
+
+
+void Fire2012()
+{
+// Array of temperature readings at each simulation cell
+ static byte heat[NUM_LEDS];
+
+ // Step 1. Cool down every cell a little
+ for( int i = 0; i < NUM_LEDS; i++) {
+ heat[i] = qsub8( heat[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
+ }
+
+ // Step 2. Heat from each cell drifts 'up' and diffuses a little
+ for( int k= NUM_LEDS - 1; k >= 2; k--) {
+ heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
+ }
+
+ // Step 3. Randomly ignite new 'sparks' of heat near the bottom
+ if( random8() < SPARKING ) {
+ int y = random8(7);
+ heat[y] = qadd8( heat[y], random8(160,255) );
+ }
+
+ // Step 4. Map from heat cells to LED colors
+ for( int j = 0; j < NUM_LEDS; j++) {
+ leds[j] = HeatColor( heat[j]);
+ }
+}
+
diff --git a/examples/Fire2012WithPalette/Fire2012WithPalette.ino b/examples/Fire2012WithPalette/Fire2012WithPalette.ino
new file mode 100644
index 00000000..41b9a247
--- /dev/null
+++ b/examples/Fire2012WithPalette/Fire2012WithPalette.ino
@@ -0,0 +1,155 @@
+#include <FastLED.h>
+
+#define LED_PIN 5
+#define COLOR_ORDER GRB
+#define CHIPSET WS2811
+#define NUM_LEDS 30
+
+#define BRIGHTNESS 200
+#define FRAMES_PER_SECOND 60
+
+CRGB leds[NUM_LEDS];
+
+// Fire2012 with programmable Color Palette
+//
+// This code is the same fire simulation as the original "Fire2012",
+// but each heat cell's temperature is translated to color through a FastLED
+// programmable color palette, instead of through the "HeatColor(...)" function.
+//
+// Four different static color palettes are provided here, plus one dynamic one.
+//
+// The three static ones are:
+// 1. the FastLED built-in HeatColors_p -- this is the default, and it looks
+// pretty much exactly like the original Fire2012.
+//
+// To use any of the other palettes below, just "uncomment" the corresponding code.
+//
+// 2. a gradient from black to red to yellow to white, which is
+// visually similar to the HeatColors_p, and helps to illustrate
+// what the 'heat colors' palette is actually doing,
+// 3. a similar gradient, but in blue colors rather than red ones,
+// i.e. from black to blue to aqua to white, which results in
+// an "icy blue" fire effect,
+// 4. a simplified three-step gradient, from black to red to white, just to show
+// that these gradients need not have four components; two or
+// three are possible, too, even if they don't look quite as nice for fire.
+//
+// The dynamic palette shows how you can change the basic 'hue' of the
+// color palette every time through the loop, producing "rainbow fire".
+
+CRGBPalette16 gPal;
+
+void setup() {
+ delay(3000); // sanity delay
+ FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
+ FastLED.setBrightness( BRIGHTNESS );
+
+ // This first palette is the basic 'black body radiation' colors,
+ // which run from black to red to bright yellow to white.
+ gPal = HeatColors_p;
+
+ // These are other ways to set up the color palette for the 'fire'.
+ // First, a gradient from black to red to yellow to white -- similar to HeatColors_p
+ // gPal = CRGBPalette16( CRGB::Black, CRGB::Red, CRGB::Yellow, CRGB::White);
+
+ // Second, this palette is like the heat colors, but blue/aqua instead of red/yellow
+ // gPal = CRGBPalette16( CRGB::Black, CRGB::Blue, CRGB::Aqua, CRGB::White);
+
+ // Third, here's a simpler, three-step gradient, from black to red to white
+ // gPal = CRGBPalette16( CRGB::Black, CRGB::Red, CRGB::White);
+
+}
+
+void loop()
+{
+ // Add entropy to random number generator; we use a lot of it.
+ random16_add_entropy( random());
+
+ // Fourth, the most sophisticated: this one sets up a new palette every
+ // time through the loop, based on a hue that changes every time.
+ // The palette is a gradient from black, to a dark color based on the hue,
+ // to a light color based on the hue, to white.
+ //
+ // static uint8_t hue = 0;
+ // hue++;
+ // CRGB darkcolor = CHSV(hue,255,192); // pure hue, three-quarters brightness
+ // CRGB lightcolor = CHSV(hue,128,255); // half 'whitened', full brightness
+ // gPal = CRGBPalette16( CRGB::Black, darkcolor, lightcolor, CRGB::White);
+
+
+ Fire2012WithPalette(); // run simulation frame, using palette colors
+
+ FastLED.show(); // display this frame
+ FastLED.delay(1000 / FRAMES_PER_SECOND);
+}
+
+
+// Fire2012 by Mark Kriegsman, July 2012
+// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY
+////
+// This basic one-dimensional 'fire' simulation works roughly as follows:
+// There's a underlying array of 'heat' cells, that model the temperature
+// at each point along the line. Every cycle through the simulation,
+// four steps are performed:
+// 1) All cells cool down a little bit, losing heat to the air
+// 2) The heat from each cell drifts 'up' and diffuses a little
+// 3) Sometimes randomly new 'sparks' of heat are added at the bottom
+// 4) The heat from each cell is rendered as a color into the leds array
+// The heat-to-color mapping uses a black-body radiation approximation.
+//
+// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
+//
+// This simulation scales it self a bit depending on NUM_LEDS; it should look
+// "OK" on anywhere from 20 to 100 LEDs without too much tweaking.
+//
+// I recommend running this simulation at anywhere from 30-100 frames per second,
+// meaning an interframe delay of about 10-35 milliseconds.
+//
+// Looks best on a high-density LED setup (60+ pixels/meter).
+//
+//
+// There are two main parameters you can play with to control the look and
+// feel of your fire: COOLING (used in step 1 above), and SPARKING (used
+// in step 3 above).
+//
+// COOLING: How much does the air cool as it rises?
+// Less cooling = taller flames. More cooling = shorter flames.
+// Default 55, suggested range 20-100
+#define COOLING 55
+
+// SPARKING: What chance (out of 255) is there that a new spark will be lit?
+// Higher chance = more roaring fire. Lower chance = more flickery fire.
+// Default 120, suggested range 50-200.
+#define SPARKING 120
+
+
+void Fire2012WithPalette()
+{
+// Array of temperature readings at each simulation cell
+ static byte heat[NUM_LEDS];
+
+ // Step 1. Cool down every cell a little
+ for( int i = 0; i < NUM_LEDS; i++) {
+ heat[i] = qsub8( heat[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
+ }
+
+ // Step 2. Heat from each cell drifts 'up' and diffuses a little
+ for( int k= NUM_LEDS - 1; k >= 2; k--) {
+ heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
+ }
+
+ // Step 3. Randomly ignite new 'sparks' of heat near the bottom
+ if( random8() < SPARKING ) {
+ int y = random8(7);
+ heat[y] = qadd8( heat[y], random8(160,255) );
+ }
+
+ // Step 4. Map from heat cells to LED colors
+ for( int j = 0; j < NUM_LEDS; j++) {
+ // 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);
+ }
+}
+
diff --git a/examples/FirstLight/FirstLight.ino b/examples/FirstLight/FirstLight.ino
index be7f33cc..928c7bbd 100644
--- a/examples/FirstLight/FirstLight.ino
+++ b/examples/FirstLight/FirstLight.ino
@@ -1,3 +1,6 @@
+// Use if you want to force the software SPI subsystem to be used for some reason (generally, you don't)
+// #define 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
#include "FastLED.h"
@@ -12,7 +15,7 @@
#define NUM_LEDS 60
// Data pin that led data will be written out over
-#define DATA_PIN 6
+#define DATA_PIN 3
// Clock pin only needed for SPI based chipsets when not using hardware SPI
//#define CLOCK_PIN 8
@@ -29,17 +32,21 @@ void setup() {
// FastLED.addLeds<TM1803, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1804, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1809, DATA_PIN, RGB>(leds, NUM_LEDS);
- // FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
+ FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS);
- FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);
- // FastLED.addLeds<NEOPIXEL, 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<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);
// FastLED.addLeds<UCS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<UCS1903B, DATA_PIN, RGB>(leds, NUM_LEDS);
// 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<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);
diff --git a/examples/Multiple/ArrayOfLedArrays/ArrayOfLedArrays.ino b/examples/Multiple/ArrayOfLedArrays/ArrayOfLedArrays.ino
new file mode 100644
index 00000000..cc171835
--- /dev/null
+++ b/examples/Multiple/ArrayOfLedArrays/ArrayOfLedArrays.ino
@@ -0,0 +1,37 @@
+// ArrayOfLedArrays - see https://github.com/FastLED/FastLED/wiki/Multiple-Controller-Examples for more info on
+// using multiple controllers. In this example, we're going to set up four NEOPIXEL strips on three
+// different pins, each strip getting its own CRGB array to be played with, only this time they're going
+// to be all parts of an array of arrays.
+
+#include "FastLED.h"
+
+#define NUM_STRIPS 3
+#define NUM_LEDS_PER_STRIP 60
+CRGB leds[NUM_STRIPS][NUM_LEDS_PER_STRIP];
+
+// For mirroring strips, all the "special" stuff happens just in setup. We
+// just addLeds multiple times, once for each strip
+void setup() {
+ // tell FastLED there's 60 NEOPIXEL leds on pin 10
+ FastLED.addLeds<NEOPIXEL, 10>(leds[0], NUM_LEDS_PER_STRIP);
+
+ // tell FastLED there's 60 NEOPIXEL leds on pin 11
+ FastLED.addLeds<NEOPIXEL, 11>(leds[1], NUM_LEDS_PER_STRIP);
+
+ // tell FastLED there's 60 NEOPIXEL leds on pin 12
+ FastLED.addLeds<NEOPIXEL, 12>(leds[2], NUM_LEDS_PER_STRIP);
+
+}
+
+void loop() {
+ // This outer loop will go over each strip, one at a time
+ for(int x = 0; x < NUM_STRIPS; x++) {
+ // This inner loop will go over each led in the current strip, one at a time
+ for(int i = 0; i < NUM_LEDS_PER_STRIP; i++) {
+ leds[x][i] = CRGB::Red;
+ FastLED.show();
+ leds[x][i] = CRGB::Black;
+ delay(100);
+ }
+ }
+}
diff --git a/examples/Multiple/MirroringSample/MirroringSample.ino b/examples/Multiple/MirroringSample/MirroringSample.ino
new file mode 100644
index 00000000..39d748bd
--- /dev/null
+++ b/examples/Multiple/MirroringSample/MirroringSample.ino
@@ -0,0 +1,44 @@
+// MirroringSample - see https://github.com/FastLED/FastLED/wiki/Multiple-Controller-Examples for more info on
+// using multiple controllers. In this example, we're going to set up four NEOPIXEL strips on four
+// different pins, and show the same thing on all four of them, a simple bouncing dot/cyclon type pattern
+
+#include "FastLED.h"
+
+#define NUM_LEDS_PER_STRIP 60
+CRGB leds[NUM_LEDS_PER_STRIP];
+
+// For mirroring strips, all the "special" stuff happens just in setup. We
+// just addLeds multiple times, once for each strip
+void setup() {
+ // tell FastLED there's 60 NEOPIXEL leds on pin 4
+ FastLED.addLeds<NEOPIXEL, 4>(leds, NUM_LEDS_PER_STRIP);
+
+ // tell FastLED there's 60 NEOPIXEL leds on pin 5
+ FastLED.addLeds<NEOPIXEL, 5>(leds, NUM_LEDS_PER_STRIP);
+
+ // tell FastLED there's 60 NEOPIXEL leds on pin 6
+ FastLED.addLeds<NEOPIXEL, 6>(leds, NUM_LEDS_PER_STRIP);
+
+ // tell FastLED there's 60 NEOPIXEL leds on pin 7
+ FastLED.addLeds<NEOPIXEL, 7>(leds, NUM_LEDS_PER_STRIP);
+}
+
+void loop() {
+ for(int i = 0; i < NUM_LEDS_PER_STRIP; i++) {
+ // set our current dot to red
+ leds[i] = CRGB::Red;
+ FastLED.show();
+ // clear our current dot before we move on
+ leds[i] = CRGB::Black;
+ delay(100);
+ }
+
+ for(int i = NUM_LEDS_PER_STRIP-1; i >= 0; i--) {
+ // set our current dot to red
+ leds[i] = CRGB::Red;
+ FastLED.show();
+ // clear our current dot before we move on
+ leds[i] = CRGB::Black;
+ delay(100);
+ }
+}
diff --git a/examples/Multiple/MultiArrays/MultiArrays.ino b/examples/Multiple/MultiArrays/MultiArrays.ino
new file mode 100644
index 00000000..79b5bfc9
--- /dev/null
+++ b/examples/Multiple/MultiArrays/MultiArrays.ino
@@ -0,0 +1,52 @@
+// MultiArrays - see https://github.com/FastLED/FastLED/wiki/Multiple-Controller-Examples for more info on
+// using multiple controllers. In this example, we're going to set up four NEOPIXEL strips on three
+// different pins, each strip getting its own CRGB array to be played with
+
+#include "FastLED.h"
+
+#define NUM_LEDS_PER_STRIP 60
+CRGB redLeds[NUM_LEDS_PER_STRIP];
+CRGB greenLeds[NUM_LEDS_PER_STRIP];
+CRGB blueLeds[NUM_LEDS_PER_STRIP];
+
+// For mirroring strips, all the "special" stuff happens just in setup. We
+// just addLeds multiple times, once for each strip
+void setup() {
+ // tell FastLED there's 60 NEOPIXEL leds on pin 10
+ FastLED.addLeds<NEOPIXEL, 10>(redLeds, NUM_LEDS_PER_STRIP);
+
+ // tell FastLED there's 60 NEOPIXEL leds on pin 11
+ FastLED.addLeds<NEOPIXEL, 11>(greenLeds, NUM_LEDS_PER_STRIP);
+
+ // tell FastLED there's 60 NEOPIXEL leds on pin 12
+ FastLED.addLeds<NEOPIXEL, 12>(blueLeds, NUM_LEDS_PER_STRIP);
+
+}
+
+void loop() {
+ for(int i = 0; i < NUM_LEDS_PER_STRIP; i++) {
+ // set our current dot to red, green, and blue
+ redLeds[i] = CRGB::Red;
+ greenLeds[i] = CRGB::Green;
+ blueLeds[i] = CRGB::Blue;
+ FastLED.show();
+ // clear our current dot before we move on
+ redLeds[i] = CRGB::Black;
+ greenLeds[i] = CRGB::Black;
+ blueLeds[i] = CRGB::Blue;
+ delay(100);
+ }
+
+ for(int i = NUM_LEDS_PER_STRIP-1; i >= 0; i--) {
+ // set our current dot to red, green, and blue
+ redLeds[i] = CRGB::Red;
+ greenLeds[i] = CRGB::Green;
+ blueLeds[i] = CRGB::Blue;
+ FastLED.show();
+ // clear our current dot before we move on
+ redLeds[i] = CRGB::Black;
+ greenLeds[i] = CRGB::Black;
+ blueLeds[i] = CRGB::Blue;
+ delay(100);
+ }
+}
diff --git a/examples/Multiple/MultipleStripsInOneArray/MultipleStripsInOneArray.ino b/examples/Multiple/MultipleStripsInOneArray/MultipleStripsInOneArray.ino
new file mode 100644
index 00000000..35a6c6a3
--- /dev/null
+++ b/examples/Multiple/MultipleStripsInOneArray/MultipleStripsInOneArray.ino
@@ -0,0 +1,34 @@
+// MultipleStripsInOneArray - see https://github.com/FastLED/FastLED/wiki/Multiple-Controller-Examples for more info on
+// using multiple controllers. In this example, we're going to set up four NEOPIXEL strips on three
+// different pins, each strip will be referring to a different part of the single led array
+
+#include "FastLED.h"
+
+#define NUM_STRIPS 3
+#define NUM_LEDS_PER_STRIP 60
+#define NUM_LEDS NUM_LEDS_PER_STRIP * NUM_STRIPS
+
+CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP];
+
+// For mirroring strips, all the "special" stuff happens just in setup. We
+// just addLeds multiple times, once for each strip
+void setup() {
+ // tell FastLED there's 60 NEOPIXEL leds on pin 10, starting at index 0 in the led array
+ FastLED.addLeds<NEOPIXEL, 10>(leds, 0, NUM_LEDS_PER_STRIP);
+
+ // tell FastLED there's 60 NEOPIXEL leds on pin 11, starting at index 60 in the led array
+ FastLED.addLeds<NEOPIXEL, 11>(leds, NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP);
+
+ // tell FastLED there's 60 NEOPIXEL leds on pin 12, starting at index 120 in the led array
+ FastLED.addLeds<NEOPIXEL, 12>(leds, 2 * NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP);
+
+}
+
+void loop() {
+ for(int i = 0; i < NUM_LEDS; i++) {
+ leds[i] = CRGB::Red;
+ FastLED.show();
+ leds[i] = CRGB::Black;
+ delay(100);
+ }
+}
diff --git a/examples/Noise/Noise.ino b/examples/Noise/Noise.ino
new file mode 100644
index 00000000..c2a64752
--- /dev/null
+++ b/examples/Noise/Noise.ino
@@ -0,0 +1,112 @@
+#include<FastLED.h>
+
+//
+// Mark's xy coordinate mapping code. See the XYMatrix for more information on it.
+//
+
+// Params for width and height
+const uint8_t kMatrixWidth = 16;
+const uint8_t kMatrixHeight = 16;
+#define MAX_DIMENSION ((kMatrixWidth>kMatrixHeight) ? kMatrixWidth : kMatrixHeight)
+#define NUM_LEDS (kMatrixWidth * kMatrixHeight)
+// Param for different pixel layouts
+const bool kMatrixSerpentineLayout = true;
+
+
+uint16_t XY( uint8_t x, uint8_t y)
+{
+ uint16_t i;
+
+ if( kMatrixSerpentineLayout == false) {
+ i = (y * kMatrixWidth) + x;
+ }
+
+ if( kMatrixSerpentineLayout == true) {
+ if( y & 0x01) {
+ // Odd rows run backwards
+ uint8_t reverseX = (kMatrixWidth - 1) - x;
+ i = (y * kMatrixWidth) + reverseX;
+ } else {
+ // Even rows run forwards
+ i = (y * kMatrixWidth) + x;
+ }
+ }
+
+ return i;
+}
+
+// The leds
+CRGB leds[kMatrixWidth * kMatrixHeight];
+
+// The 32bit version of our coordinates
+static uint16_t x;
+static uint16_t y;
+static uint16_t z;
+
+// We're using the x/y dimensions to map to the x/y pixels on the matrix. We'll
+// use the z-axis for "time". speed determines how fast time moves forward. Try
+// 1 for a very slow moving effect, or 60 for something that ends up looking like
+// water.
+// uint16_t speed = 1; // almost looks like a painting, moves very slowly
+uint16_t speed = 20; // a nice starting speed, mixes well with a scale of 100
+// uint16_t speed = 33;
+// uint16_t speed = 100; // wicked fast!
+
+// Scale determines how far apart the pixels in our noise matrix are. Try
+// changing these values around to see how it affects the motion of the display. The
+// higher the value of scale, the more "zoomed out" the noise iwll be. A value
+// of 1 will be so zoomed in, you'll mostly see solid colors.
+
+// uint16_t scale = 1; // mostly just solid colors
+// uint16_t scale = 4011; // very zoomed out and shimmery
+uint16_t scale = 311;
+
+// This is the array that we keep our computed noise values in
+uint8_t noise[MAX_DIMENSION][MAX_DIMENSION];
+
+void setup() {
+ // uncomment the following lines if you want to see FPS count information
+ // Serial.begin(38400);
+ // Serial.println("resetting!");
+ delay(3000);
+ LEDS.addLeds<WS2811,5,RGB>(leds,NUM_LEDS);
+ LEDS.setBrightness(96);
+
+ // Initialize our coordinates to some random values
+ x = random16();
+ y = random16();
+ z = random16();
+}
+
+// Fill the x/y array of 8-bit noise values using the inoise8 function.
+void fillnoise8() {
+ for(int i = 0; i < MAX_DIMENSION; i++) {
+ int ioffset = scale * i;
+ for(int j = 0; j < MAX_DIMENSION; j++) {
+ int joffset = scale * j;
+ noise[i][j] = inoise8(x + ioffset,y + joffset,z);
+ }
+ }
+ z += speed;
+}
+
+
+void loop() {
+ static uint8_t ihue=0;
+ fillnoise8();
+ for(int i = 0; i < kMatrixWidth; i++) {
+ for(int j = 0; j < kMatrixHeight; j++) {
+ // We use the value at the (i,j) coordinate in the noise
+ // array for our brightness, and the flipped value from (j,i)
+ // for our pixel's hue.
+ leds[XY(i,j)] = CHSV(noise[j][i],255,noise[i][j]);
+
+ // You can also explore other ways to constrain the hue used, like below
+ // leds[XY(i,j)] = CHSV(ihue + (noise[j][i]>>2),255,noise[i][j]);
+ }
+ }
+ ihue+=1;
+
+ LEDS.show();
+ // delay(10);
+}
diff --git a/examples/NoisePlayground/NoisePlayground.ino b/examples/NoisePlayground/NoisePlayground.ino
new file mode 100644
index 00000000..e2c7cb31
--- /dev/null
+++ b/examples/NoisePlayground/NoisePlayground.ino
@@ -0,0 +1,73 @@
+#include <FastLED.h>
+
+#define kMatrixWidth 16
+#define kMatrixHeight 16
+
+#define NUM_LEDS (kMatrixWidth * kMatrixHeight)
+// Param for different pixel layouts
+#define kMatrixSerpentineLayout true
+
+// led array
+CRGB leds[kMatrixWidth * kMatrixHeight];
+
+// x,y, & time values
+uint32_t x,y,v_time,hue_time,hxy;
+
+// Play with the values of the variables below and see what kinds of effects they
+// have! More octaves will make things slower.
+
+// how many octaves to use for the brightness and hue functions
+uint8_t octaves=1;
+uint8_t hue_octaves=3;
+
+// the 'distance' between points on the x and y axis
+int xscale=57771;
+int yscale=57771;
+
+// the 'distance' between x/y points for the hue noise
+int hue_scale=1;
+
+// how fast we move through time & hue noise
+int time_speed=1111;
+int hue_speed=31;
+
+// adjust these values to move along the x or y axis between frames
+int x_speed=331;
+int y_speed=1111;
+
+void loop() {
+ // fill the led array 2/16-bit noise values
+ fill_2dnoise16(LEDS.leds(), kMatrixWidth, kMatrixHeight, kMatrixSerpentineLayout,
+ octaves,x,xscale,y,yscale,v_time,
+ hue_octaves,hxy,hue_scale,hxy,hue_scale,hue_time, false);
+
+ LEDS.show();
+
+ // adjust the intra-frame time values
+ x += x_speed;
+ y += y_speed;
+ v_time += time_speed;
+ hue_time += hue_speed;
+ // delay(50);
+}
+
+
+void setup() {
+ // initialize the x/y and time values
+ random16_set_seed(8934);
+ random16_add_entropy(analogRead(3));
+
+ Serial.begin(57600);
+ Serial.println("resetting!");
+
+ delay(3000);
+ LEDS.addLeds<WS2811,6,GRB>(leds,NUM_LEDS);
+ LEDS.setBrightness(96);
+
+ hxy = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
+ x = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
+ y = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
+ v_time = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
+ hue_time = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16();
+
+}
diff --git a/examples/NoisePlusPalette/NoisePlusPalette.ino b/examples/NoisePlusPalette/NoisePlusPalette.ino
new file mode 100644
index 00000000..4e06bdd8
--- /dev/null
+++ b/examples/NoisePlusPalette/NoisePlusPalette.ino
@@ -0,0 +1,273 @@
+#include<FastLED.h>
+
+#define LED_PIN 3
+#define BRIGHTNESS 96
+#define LED_TYPE WS2811
+#define COLOR_ORDER GRB
+
+const uint8_t kMatrixWidth = 16;
+const uint8_t kMatrixHeight = 16;
+const bool kMatrixSerpentineLayout = true;
+
+
+// This example combines two features of FastLED to produce a remarkable range of
+// effects from a relatively small amount of code. This example combines FastLED's
+// color palette lookup functions with FastLED's Perlin/simplex noise generator, and
+// the combination is extremely powerful.
+//
+// You might want to look at the "ColorPalette" and "Noise" examples separately
+// if this example code seems daunting.
+//
+//
+// The basic setup here is that for each frame, we generate a new array of
+// 'noise' data, and then map it onto the LED matrix through a color palette.
+//
+// Periodically, the color palette is changed, and new noise-generation parameters
+// are chosen at the same time. In this example, specific noise-generation
+// values have been selected to match the given color palettes; some are faster,
+// or slower, or larger, or smaller than others, but there's no reason these
+// parameters can't be freely mixed-and-matched.
+//
+// In addition, this example includes some fast automatic 'data smoothing' at
+// lower noise speeds to help produce smoother animations in those cases.
+//
+// The FastLED built-in color palettes (Forest, Clouds, Lava, Ocean, Party) are
+// used, as well as some 'hand-defined' ones, and some proceedurally generated
+// palettes.
+
+
+#define NUM_LEDS (kMatrixWidth * kMatrixHeight)
+#define MAX_DIMENSION ((kMatrixWidth>kMatrixHeight) ? kMatrixWidth : kMatrixHeight)
+
+// The leds
+CRGB leds[kMatrixWidth * kMatrixHeight];
+
+// The 16 bit version of our coordinates
+static uint16_t x;
+static uint16_t y;
+static uint16_t z;
+
+// We're using the x/y dimensions to map to the x/y pixels on the matrix. We'll
+// use the z-axis for "time". speed determines how fast time moves forward. Try
+// 1 for a very slow moving effect, or 60 for something that ends up looking like
+// water.
+uint16_t speed = 20; // speed is set dynamically once we've started up
+
+// Scale determines how far apart the pixels in our noise matrix are. Try
+// changing these values around to see how it affects the motion of the display. The
+// higher the value of scale, the more "zoomed out" the noise iwll be. A value
+// of 1 will be so zoomed in, you'll mostly see solid colors.
+uint16_t scale = 30; // scale is set dynamically once we've started up
+
+// This is the array that we keep our computed noise values in
+uint8_t noise[MAX_DIMENSION][MAX_DIMENSION];
+
+CRGBPalette16 currentPalette( PartyColors_p );
+uint8_t colorLoop = 1;
+
+void setup() {
+ delay(3000);
+ LEDS.addLeds<LED_TYPE,LED_PIN,COLOR_ORDER>(leds,NUM_LEDS);
+ LEDS.setBrightness(BRIGHTNESS);
+
+ // Initialize our coordinates to some random values
+ x = random16();
+ y = random16();
+ z = random16();
+}
+
+
+
+// Fill the x/y array of 8-bit noise values using the inoise8 function.
+void fillnoise8() {
+ // If we're runing at a low "speed", some 8-bit artifacts become visible
+ // from frame-to-frame. In order to reduce this, we can do some fast data-smoothing.
+ // The amount of data smoothing we're doing depends on "speed".
+ uint8_t dataSmoothing = 0;
+ if( speed < 50) {
+ dataSmoothing = 200 - (speed * 4);
+ }
+
+ for(int i = 0; i < MAX_DIMENSION; i++) {
+ int ioffset = scale * i;
+ for(int j = 0; j < MAX_DIMENSION; j++) {
+ int joffset = scale * j;
+
+ uint8_t data = inoise8(x + ioffset,y + joffset,z);
+
+ // The range of the inoise8 function is roughly 16-238.
+ // These two operations expand those values out to roughly 0..255
+ // You can comment them out if you want the raw noise data.
+ data = qsub8(data,16);
+ data = qadd8(data,scale8(data,39));
+
+ if( dataSmoothing ) {
+ uint8_t olddata = noise[i][j];
+ uint8_t newdata = scale8( olddata, dataSmoothing) + scale8( data, 256 - dataSmoothing);
+ data = newdata;
+ }
+
+ noise[i][j] = data;
+ }
+ }
+
+ z += speed;
+
+ // apply slow drift to X and Y, just for visual variation.
+ x += speed / 8;
+ y -= speed / 16;
+}
+
+void mapNoiseToLEDsUsingPalette()
+{
+ static uint8_t ihue=0;
+
+ for(int i = 0; i < kMatrixWidth; i++) {
+ for(int j = 0; j < kMatrixHeight; j++) {
+ // We use the value at the (i,j) coordinate in the noise
+ // array for our brightness, and the flipped value from (j,i)
+ // for our pixel's index into the color palette.
+
+ uint8_t index = noise[j][i];
+ uint8_t bri = noise[i][j];
+
+ // if this palette is a 'loop', add a slowly-changing base value
+ if( colorLoop) {
+ index += ihue;
+ }
+
+ // brighten up, as the color palette itself often contains the
+ // light/dark dynamic range desired
+ if( bri > 127 ) {
+ bri = 255;
+ } else {
+ bri = dim8_raw( bri * 2);
+ }
+
+ CRGB color = ColorFromPalette( currentPalette, index, bri);
+ leds[XY(i,j)] = color;
+ }
+ }
+
+ ihue+=1;
+}
+
+void loop() {
+ // Periodically choose a new palette, speed, and scale
+ ChangePaletteAndSettingsPeriodically();
+
+ // generate noise data
+ fillnoise8();
+
+ // convert the noise data to colors in the LED array
+ // using the current palette
+ mapNoiseToLEDsUsingPalette();
+
+ LEDS.show();
+ // delay(10);
+}
+
+
+
+// There are several different palettes of colors demonstrated here.
+//
+// FastLED provides several 'preset' palettes: RainbowColors_p, RainbowStripeColors_p,
+// OceanColors_p, CloudColors_p, LavaColors_p, ForestColors_p, and PartyColors_p.
+//
+// Additionally, you can manually define your own color palettes, or you can write
+// code that creates color palettes on the fly.
+
+// 1 = 5 sec per palette
+// 2 = 10 sec per palette
+// etc
+#define HOLD_PALETTES_X_TIMES_AS_LONG 1
+
+void ChangePaletteAndSettingsPeriodically()
+{
+ uint8_t secondHand = ((millis() / 1000) / HOLD_PALETTES_X_TIMES_AS_LONG) % 60;
+ static uint8_t lastSecond = 99;
+
+ if( lastSecond != secondHand) {
+ lastSecond = secondHand;
+ if( secondHand == 0) { currentPalette = RainbowColors_p; speed = 20; scale = 30; colorLoop = 1; }
+ if( secondHand == 5) { SetupPurpleAndGreenPalette(); speed = 10; scale = 50; colorLoop = 1; }
+ if( secondHand == 10) { SetupBlackAndWhiteStripedPalette(); speed = 20; scale = 30; colorLoop = 1; }
+ if( secondHand == 15) { currentPalette = ForestColors_p; speed = 8; scale =120; colorLoop = 0; }
+ if( secondHand == 20) { currentPalette = CloudColors_p; speed = 4; scale = 30; colorLoop = 0; }
+ if( secondHand == 25) { currentPalette = LavaColors_p; speed = 8; scale = 50; colorLoop = 0; }
+ if( secondHand == 30) { currentPalette = OceanColors_p; speed = 20; scale = 90; colorLoop = 0; }
+ if( secondHand == 35) { currentPalette = PartyColors_p; speed = 20; scale = 30; colorLoop = 1; }
+ if( secondHand == 40) { SetupRandomPalette(); speed = 20; scale = 20; colorLoop = 1; }
+ if( secondHand == 45) { SetupRandomPalette(); speed = 50; scale = 50; colorLoop = 1; }
+ if( secondHand == 50) { SetupRandomPalette(); speed = 90; scale = 90; colorLoop = 1; }
+ if( secondHand == 55) { currentPalette = RainbowStripeColors_p; speed = 30; scale = 20; colorLoop = 1; }
+ }
+}
+
+// This function generates a random palette that's a gradient
+// between four different colors. The first is a dim hue, the second is
+// a bright hue, the third is a bright pastel, and the last is
+// another bright hue. This gives some visual bright/dark variation
+// which is more interesting than just a gradient of different hues.
+void SetupRandomPalette()
+{
+ currentPalette = CRGBPalette16(
+ CHSV( random8(), 255, 32),
+ CHSV( random8(), 255, 255),
+ CHSV( random8(), 128, 255),
+ CHSV( random8(), 255, 255));
+}
+
+// This function sets up a palette of black and white stripes,
+// using code. Since the palette is effectively an array of
+// sixteen CRGB colors, the various fill_* functions can be used
+// 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;
+
+}
+
+// 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 );
+}
+
+
+//
+// Mark's xy coordinate mapping code. See the XYMatrix for more information on it.
+//
+uint16_t XY( uint8_t x, uint8_t y)
+{
+ uint16_t i;
+ if( kMatrixSerpentineLayout == false) {
+ i = (y * kMatrixWidth) + x;
+ }
+ if( kMatrixSerpentineLayout == true) {
+ if( y & 0x01) {
+ // Odd rows run backwards
+ uint8_t reverseX = (kMatrixWidth - 1) - x;
+ i = (y * kMatrixWidth) + reverseX;
+ } else {
+ // Even rows run forwards
+ i = (y * kMatrixWidth) + x;
+ }
+ }
+ return i;
+}
+
diff --git a/examples/Pintest/Pintest.ino b/examples/Pintest/Pintest.ino
new file mode 100644
index 00000000..727341e7
--- /dev/null
+++ b/examples/Pintest/Pintest.ino
@@ -0,0 +1,105 @@
+
+#include <FastSPI_LED2.h>
+
+const char *getPort(void *portPtr) {
+#ifdef PORTA
+ if(portPtr == (void*)&PORTA) { return "PORTA"; }
+#endif
+#ifdef PORTB
+ if(portPtr == (void*)&PORTB) { return "PORTB"; }
+#endif
+#ifdef PORTC
+ if(portPtr == (void*)&PORTC) { return "PORTC"; }
+#endif
+#ifdef PORTD
+ if(portPtr == (void*)&PORTD) { return "PORTD"; }
+#endif
+#ifdef PORTE
+ if(portPtr == (void*)&PORTE) { return "PORTE"; }
+#endif
+#ifdef PORTF
+ if(portPtr == (void*)&PORTF) { return "PORTF"; }
+#endif
+#ifdef PORTG
+ if(portPtr == (void*)&PORTG) { return "PORTG"; }
+#endif
+#ifdef PORTH
+ if(portPtr == (void*)&PORTH) { return "PORTH"; }
+#endif
+#ifdef PORTI
+ if(portPtr == (void*)&PORTI) { return "PORTI"; }
+#endif
+#ifdef PORTJ
+ if(portPtr == (void*)&PORTJ) { return "PORTJ"; }
+#endif
+#ifdef PORTK
+ if(portPtr == (void*)&PORTK) { return "PORTK"; }
+#endif
+#ifdef PORTL
+ if(portPtr == (void*)&PORTL) { return "PORTL"; }
+#endif
+#ifdef GPIO_A_PDOR
+ if(portPtr == (void*)&GPIO_A_PDOR) { return "GPIO_A_PDOR"; }
+#endif
+#ifdef GPIO_B_PDOR
+ if(portPtr == (void*)&GPIO_B_PDOR) { return "GPIO_B_PDOR"; }
+#endif
+#ifdef GPIO_C_PDOR
+ if(portPtr == (void*)&GPIO_C_PDOR) { return "GPIO_C_PDOR"; }
+#endif
+#ifdef GPIO_D_PDOR
+ if(portPtr == (void*)&GPIO_D_PDOR) { return "GPIO_D_PDOR"; }
+#endif
+#ifdef GPIO_E_PDOR
+ if(portPtr == (void*)&GPIO_E_PDOR) { return "GPIO_E_PDOR"; }
+#endif
+#ifdef REG_PIO_A_ODSR
+ if(portPtr == (void*)&REG_PIO_A_ODSR) { return "REG_PIO_A_ODSR"; }
+#endif
+#ifdef REG_PIO_B_ODSR
+ if(portPtr == (void*)&REG_PIO_B_ODSR) { return "REG_PIO_B_ODSR"; }
+#endif
+#ifdef REG_PIO_C_ODSR
+ if(portPtr == (void*)&REG_PIO_C_ODSR) { return "REG_PIO_C_ODSR"; }
+#endif
+#ifdef REG_PIO_D_ODSR
+ if(portPtr == (void*)&REG_PIO_D_ODSR) { return "REG_PIO_D_ODSR"; }
+#endif
+ return "unknown";
+}
+
+template<uint8_t PIN> void CheckPin()
+{
+ CheckPin<PIN - 1>();
+
+ RwReg *systemThinksPortIs = portOutputRegister(digitalPinToPort(PIN));
+ RwReg systemThinksMaskIs = digitalPinToBitMask(PIN);
+
+ Serial.print("Pin "); Serial.print(PIN); Serial.print(": Port ");
+
+ if(systemThinksPortIs == FastPin<PIN>::port()) {
+ Serial.print("valid & mask ");
+ } else {
+ Serial.print("invalid, is "); Serial.print(getPort((void*)FastPin<PIN>::port())); Serial.print(" should be ");
+ Serial.print(getPort((void*)systemThinksPortIs));
+ Serial.print(" & mask ");
+ }
+
+ if(systemThinksMaskIs == FastPin<PIN>::mask()) {
+ Serial.println("valid.");
+ } else {
+ Serial.print("invalid, is "); Serial.print(FastPin<PIN>::mask()); Serial.print(" should be "); Serial.println(systemThinksMaskIs);
+ }
+}
+
+template<> void CheckPin<-1> () {}
+
+void setup() {
+ Serial.begin(38400);
+ Serial.println("resetting!");
+}
+
+void loop() {
+ CheckPin<MAX_PIN>();
+ delay(10000);
+} \ No newline at end of file
diff --git a/examples/RGBCalibrate/RGBCalibrate.ino b/examples/RGBCalibrate/RGBCalibrate.ino
index 55661052..897002bf 100644
--- a/examples/RGBCalibrate/RGBCalibrate.ino
+++ b/examples/RGBCalibrate/RGBCalibrate.ino
@@ -26,7 +26,7 @@
#define NUM_LEDS 6
// Data pin that led data will be written out over
-#define DATA_PIN 7
+#define DATA_PIN 6
// Clock pin only needed for SPI based chipsets when not using hardware SPI
//#define CLOCK_PIN 8
@@ -42,12 +42,16 @@ void setup() {
// FastLED.addLeds<TM1809, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS);
- // FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
+ // FastLED.setBrightness(CRGB(255,255,255));
+ // FastLED.addLeds<GW6205, DATA_PIN, RGB>(leds, NUM_LEDS);
+ // FastLED.addLeds<GW6205_400, 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<WS2801, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SM16716, RGB>(leds, NUM_LEDS);
- // FastLED.addLeds<LPD8806, RGB>(leds, NUM_LEDS);
+ FastLED.addLeds<LPD8806, 9, 10, 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);
@@ -55,12 +59,15 @@ void setup() {
}
void loop() {
- leds[0] = CRGB::Red;
- leds[1] = CRGB::Green;
- leds[2] = CRGB::Green;
- leds[3] = CRGB::Blue;
- leds[4] = CRGB::Blue;
- leds[5] = CRGB::Blue;
+ leds[0] = CRGB(255,0,0);
+ leds[1] = CRGB(0,255,0);
+ leds[2] = CRGB(0,255,0);
+ leds[3] = CRGB(0,0,255);
+ leds[4] = CRGB(0,0,255);
+ leds[5] = CRGB(0,0,255);
+ leds[random8()%NUM_LEDS] = CRGB(0,0,0);
+ // leds[10] = CRGB(0,0,0);
FastLED.show();
- delay(1000);
+ // delay(1000);
+ FastLED.showColor(CRGB::Black);
}
diff --git a/examples/SmartMatrix/SmartMatrix.ino b/examples/SmartMatrix/SmartMatrix.ino
new file mode 100644
index 00000000..997b6d24
--- /dev/null
+++ b/examples/SmartMatrix/SmartMatrix.ino
@@ -0,0 +1,121 @@
+#include<SmartMatrix.h>
+#include<FastLED.h>
+
+#define kMatrixWidth 32
+#define kMatrixHeight 32
+const bool kMatrixSerpentineLayout = false;
+
+#define NUM_LEDS (kMatrixWidth * kMatrixHeight)
+
+CRGB leds[kMatrixWidth * kMatrixHeight];
+
+
+uint16_t XY( uint8_t x, uint8_t y)
+{
+ uint16_t i;
+
+ if( kMatrixSerpentineLayout == false) {
+ i = (y * kMatrixWidth) + x;
+ }
+
+ if( kMatrixSerpentineLayout == true) {
+ if( y & 0x01) {
+ // Odd rows run backwards
+ uint8_t reverseX = (kMatrixWidth - 1) - x;
+ i = (y * kMatrixWidth) + reverseX;
+ } else {
+ // Even rows run forwards
+ i = (y * kMatrixWidth) + x;
+ }
+ }
+
+ return i;
+}
+
+// The 32bit version of our coordinates
+static uint16_t x;
+static uint16_t y;
+static uint16_t z;
+
+// We're using the x/y dimensions to map to the x/y pixels on the matrix. We'll
+// use the z-axis for "time". speed determines how fast time moves forward. Try
+// 1 for a very slow moving effect, or 60 for something that ends up looking like
+// water.
+// uint16_t speed = 1; // almost looks like a painting, moves very slowly
+uint16_t speed = 20; // a nice starting speed, mixes well with a scale of 100
+// uint16_t speed = 33;
+// uint16_t speed = 100; // wicked fast!
+
+// Scale determines how far apart the pixels in our noise matrix are. Try
+// changing these values around to see how it affects the motion of the display. The
+// higher the value of scale, the more "zoomed out" the noise iwll be. A value
+// of 1 will be so zoomed in, you'll mostly see solid colors.
+
+// uint16_t scale = 1; // mostly just solid colors
+// uint16_t scale = 4011; // very zoomed out and shimmery
+uint16_t scale = 31;
+
+// This is the array that we keep our computed noise values in
+uint8_t noise[kMatrixWidth][kMatrixHeight];
+
+void setup() {
+ // uncomment the following lines if you want to see FPS count information
+ // Serial.begin(38400);
+ // Serial.println("resetting!");
+ delay(3000);
+ LEDS.addLeds<SMART_MATRIX>(leds,NUM_LEDS);
+ LEDS.setBrightness(96);
+
+ // Initialize our coordinates to some random values
+ x = random16();
+ y = random16();
+ z = random16();
+
+ // Show off smart matrix scrolling text
+ pSmartMatrix->setScrollMode(wrapForward);
+ pSmartMatrix->setScrollColor({0xff, 0xff, 0xff});
+ pSmartMatrix->setScrollSpeed(15);
+ pSmartMatrix->setScrollFont(font6x10);
+ pSmartMatrix->scrollText("Smart Matrix & FastLED", -1);
+ pSmartMatrix->setScrollOffsetFromEdge(10);
+}
+
+// Fill the x/y array of 8-bit noise values using the inoise8 function.
+void fillnoise8() {
+ for(int i = 0; i < kMatrixWidth; i++) {
+ int ioffset = scale * i;
+ for(int j = 0; j < kMatrixHeight; j++) {
+ int joffset = scale * j;
+ noise[i][j] = inoise8(x + ioffset,y + joffset,z);
+ }
+ }
+ z += speed;
+}
+
+
+void loop() {
+ static uint8_t circlex = 0;
+ static uint8_t circley = 0;
+
+ static uint8_t ihue=0;
+ fillnoise8();
+ for(int i = 0; i < kMatrixWidth; i++) {
+ for(int j = 0; j < kMatrixHeight; j++) {
+ // We use the value at the (i,j) coordinate in the noise
+ // array for our brightness, and the flipped value from (j,i)
+ // for our pixel's hue.
+ leds[XY(i,j)] = CHSV(noise[j][i],255,noise[i][j]);
+
+ // You can also explore other ways to constrain the hue used, like below
+ // leds[XY(i,j)] = CHSV(ihue + (noise[j][i]>>2),255,noise[i][j]);
+ }
+ }
+ ihue+=1;
+
+ // N.B. this requires SmartMatrix modified w/triple buffering support
+ pSmartMatrix->fillCircle(circlex % 32,circley % 32,6,CRGB(CHSV(ihue+128,255,255)));
+ circlex += random16(2);
+ circley += random16(2);
+ LEDS.show();
+ // delay(10);
+}
diff --git a/examples/XYMatrix/XYMatrix.ino b/examples/XYMatrix/XYMatrix.ino
new file mode 100644
index 00000000..ff0c5d14
--- /dev/null
+++ b/examples/XYMatrix/XYMatrix.ino
@@ -0,0 +1,197 @@
+#include <FastLED.h>
+
+#define LED_PIN 3
+
+#define COLOR_ORDER GRB
+#define CHIPSET WS2811
+
+#define BRIGHTNESS 64
+
+// Helper functions for an two-dimensional XY matrix of pixels.
+// Simple 2-D demo code is included as well.
+//
+// XY(x,y) takes x and y coordinates and returns an LED index number,
+// for use like this: leds[ XY(x,y) ] == CRGB::Red;
+// No error checking is performed on the ranges of x and y.
+//
+// XYsafe(x,y) takes x and y coordinates and returns an LED index number,
+// for use like this: leds[ XY(x,y) ] == CRGB::Red;
+// Error checking IS performed on the ranges of x and y, and an
+// index of "-1" is returned. Special instructions below
+// explain how to use this without having to do your own error
+// checking every time you use this function.
+// This is a slightly more advanced technique, and
+// it REQUIRES SPECIAL ADDITIONAL setup, described below.
+
+
+// Params for width and height
+const uint8_t kMatrixWidth = 16;
+const uint8_t kMatrixHeight = 16;
+
+// Param for different pixel layouts
+const bool kMatrixSerpentineLayout = true;
+// Set 'kMatrixSerpentineLayout' to false if your pixels are
+// laid out all running the same way, like this:
+//
+// 0 > 1 > 2 > 3 > 4
+// |
+// .----<----<----<----'
+// |
+// 5 > 6 > 7 > 8 > 9
+// |
+// .----<----<----<----'
+// |
+// 10 > 11 > 12 > 13 > 14
+// |
+// .----<----<----<----'
+// |
+// 15 > 16 > 17 > 18 > 19
+//
+// Set 'kMatrixSerpentineLayout' to true if your pixels are
+// laid out back-and-forth, like this:
+//
+// 0 > 1 > 2 > 3 > 4
+// |
+// |
+// 9 < 8 < 7 < 6 < 5
+// |
+// |
+// 10 > 11 > 12 > 13 > 14
+// |
+// |
+// 19 < 18 < 17 < 16 < 15
+//
+// Bonus vocabulary word: anything that goes one way
+// in one row, and then backwards in the next row, and so on
+// is call "boustrophedon", meaning "as the ox plows."
+
+
+// This function will return the right 'led index number' for
+// a given set of X and Y coordinates on your matrix.
+// IT DOES NOT CHECK THE COORDINATE BOUNDARIES.
+// That's up to you. Don't pass it bogus values.
+//
+// Use the "XY" function like this:
+//
+// for( uint8_t x = 0; x < kMatrixWidth; x++) {
+// for( uint8_t y = 0; y < kMatrixHeight; y++) {
+//
+// // Here's the x, y to 'led index' in action:
+// leds[ XY( x, y) ] = CHSV( random8(), 255, 255);
+//
+// }
+// }
+//
+//
+uint16_t XY( uint8_t x, uint8_t y)
+{
+ uint16_t i;
+
+ if( kMatrixSerpentineLayout == false) {
+ i = (y * kMatrixWidth) + x;
+ }
+
+ if( kMatrixSerpentineLayout == true) {
+ if( y & 0x01) {
+ // Odd rows run backwards
+ uint8_t reverseX = (kMatrixWidth - 1) - x;
+ i = (y * kMatrixWidth) + reverseX;
+ } else {
+ // Even rows run forwards
+ i = (y * kMatrixWidth) + x;
+ }
+ }
+
+ return i;
+}
+
+
+// Once you've gotten the basics working (AND NOT UNTIL THEN!)
+// here's a helpful technique that can be tricky to set up, but
+// then helps you avoid the needs for sprinkling array-bound-checking
+// throughout your code.
+//
+// It requires a careful attention to get it set up correctly, but
+// can potentially make your code smaller and faster.
+//
+// Suppose you have an 8 x 5 matrix of 40 LEDs. Normally, you'd
+// delcare your leds array like this:
+// CRGB leds[40];
+// But instead of that, declare an LED buffer with one extra pixel in
+// it, "leds_plus_safety_pixel". Then declare "leds" as a pointer to
+// that array, but starting with the 2nd element (id=1) of that array:
+// CRGB leds_with_safety_pixel[41];
+// const CRGB* leds( leds_plus_safety_pixel + 1);
+// Then you use the "leds" array as you normally would.
+// Now "leds[0..N]" are aliases for "leds_plus_safety_pixel[1..(N+1)]",
+// AND leds[-1] is now a legitimate and safe alias for leds_plus_safety_pixel[0].
+// leds_plus_safety_pixel[0] aka leds[-1] is now your "safety pixel".
+//
+// Now instead of using the XY function above, use the one below, "XYsafe".
+//
+// If the X and Y values are 'in bounds', this function will return an index
+// into the visible led array, same as "XY" does.
+// HOWEVER -- and this is the trick -- if the X or Y values
+// are out of bounds, this function will return an index of -1.
+// And since leds[-1] is actually just an alias for leds_plus_safety_pixel[0],
+// it's a totally safe and legal place to access. And since the 'safety pixel'
+// falls 'outside' the visible part of the LED array, anything you write
+// there is hidden from view automatically.
+// Thus, this line of code is totally safe, regardless of the actual size of
+// your matrix:
+// leds[ XYsafe( random8(), random8() ) ] = CHSV( random8(), 255, 255);
+//
+// The only catch here is that while this makes it safe to read from and
+// write to 'any pixel', there's really only ONE 'safety pixel'. No matter
+// what out-of-bounds coordinates you write to, you'll really be writing to
+// that one safety pixel. And if you try to READ from the safety pixel,
+// you'll read whatever was written there last, reglardless of what coordinates
+// were supplied.
+
+#define NUM_LEDS (kMatrixWidth * kMatrixHeight)
+CRGB leds_plus_safety_pixel[ NUM_LEDS + 1];
+CRGB* leds( leds_plus_safety_pixel + 1);
+
+uint16_t XYsafe( uint8_t x, uint8_t y)
+{
+ if( x >= kMatrixWidth) return -1;
+ if( y >= kMatrixHeight) return -1;
+ return XY(x,y);
+}
+
+
+// Demo that USES "XY" follows code below
+
+void loop()
+{
+ uint32_t ms = millis();
+ int32_t yHueDelta32 = ((int32_t)cos16( ms * (27/1) ) * (350 / kMatrixWidth));
+ int32_t xHueDelta32 = ((int32_t)cos16( ms * (39/1) ) * (310 / kMatrixHeight));
+ DrawOneFrame( ms / 65536, yHueDelta32 / 32768, xHueDelta32 / 32768);
+ if( ms < 5000 ) {
+ FastLED.setBrightness( scale8( BRIGHTNESS, (ms * 256) / 5000));
+ } else {
+ FastLED.setBrightness(BRIGHTNESS);
+ }
+ FastLED.show();
+}
+
+void DrawOneFrame( byte startHue8, int8_t yHueDelta8, int8_t xHueDelta8)
+{
+ byte lineStartHue = startHue8;
+ for( byte y = 0; y < kMatrixHeight; y++) {
+ lineStartHue += yHueDelta8;
+ byte pixelHue = lineStartHue;
+ for( byte x = 0; x < kMatrixWidth; x++) {
+ pixelHue += xHueDelta8;
+ leds[ XY(x, y)] = CHSV( pixelHue, 255, 255);
+ }
+ }
+}
+
+
+void setup() {
+ FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalSMD5050);
+ FastLED.setBrightness( BRIGHTNESS );
+}
+
diff --git a/fastpin.h b/fastpin.h
index e80f042c..56a9ea61 100644
--- a/fastpin.h
+++ b/fastpin.h
@@ -1,42 +1,16 @@
#ifndef __INC_FASTPIN_H
#define __INC_FASTPIN_H
-#include<avr/io.h>
-
-// Arduino.h needed for convinience functions digitalPinToPort/BitMask/portOutputRegister and the pinMode methods.
-#include<Arduino.h>
+#include "led_sysdefs.h"
#define NO_PIN 255
-// 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 {
- long mLastMicros;
-public:
- CMinWait() { mLastMicros = 0; }
-
- void wait() {
- long diff = micros() - mLastMicros;
- while(diff < WAIT) {
- diff = micros() - mLastMicros;
- }
- }
-
- void mark() { mLastMicros = micros(); }
-};
-
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Pin access class - needs to tune for various platforms (naive fallback solution?)
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
-#define _CYCLES(_PIN) (((_PIN >= 62 ) || (_PIN>=42 && _PIN<=49) || (_PIN>=14 && _PIN <=17) || (_PIN>=6 && _PIN <=9)) ? 2 : 1)
-#else
-#define _CYCLES(_PIN) ((_PIN >= 24) ? 2 : 1)
-#endif
-
class Selectable {
public:
virtual void select() = 0;
@@ -45,19 +19,21 @@ public:
};
class Pin : public Selectable {
- uint8_t mPinMask;
+ volatile RwReg *mPort;
+ volatile RoReg *mInPort;
+ RwReg mPinMask;
uint8_t mPin;
- volatile uint8_t *mPort;
void _init() {
mPinMask = digitalPinToBitMask(mPin);
mPort = portOutputRegister(digitalPinToPort(mPin));
+ mInPort = portInputRegister(digitalPinToPort(mPin));
}
public:
Pin(int pin) : mPin(pin) { _init(); }
- typedef volatile uint8_t * port_ptr_t;
- typedef uint8_t port_t;
+ typedef volatile RwReg * port_ptr_t;
+ typedef RwReg port_t;
inline void setOutput() { pinMode(mPin, OUTPUT); }
inline void setInput() { pinMode(mPin, INPUT); }
@@ -65,7 +41,8 @@ public:
inline void hi() __attribute__ ((always_inline)) { *mPort |= mPinMask; }
inline void lo() __attribute__ ((always_inline)) { *mPort &= ~mPinMask; }
- inline void strobe() __attribute__ ((always_inline)) { hi(); lo(); }
+ 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; }
@@ -108,15 +85,17 @@ public:
/// 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 {
- static uint8_t sPinMask;
- static volatile uint8_t *sPort;
+ static RwReg sPinMask;
+ static volatile RwReg *sPort;
+ static volatile RoReg *sInPort;
static void _init() {
sPinMask = digitalPinToBitMask(PIN);
sPort = portOutputRegister(digitalPinToPort(PIN));
+ sInPort = portInputRegister(digitalPinToPort(PIN));
}
public:
- typedef volatile uint8_t * port_ptr_t;
- typedef uint8_t port_t;
+ typedef volatile RwReg * port_ptr_t;
+ typedef RwReg port_t;
inline static void setOutput() { _init(); pinMode(PIN, OUTPUT); }
inline static void setInput() { _init(); pinMode(PIN, INPUT); }
@@ -124,7 +103,9 @@ public:
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)) { hi(); lo(); }
+ 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; }
@@ -138,118 +119,16 @@ public:
static port_t mask() __attribute__ ((always_inline)) { return sPinMask; }
};
-template<uint8_t PIN> uint8_t FastPin<PIN>::sPinMask;
-template<uint8_t PIN> volatile uint8_t *FastPin<PIN>::sPort;
-
-/// 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 {
-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; }
-
- inline static void hi() __attribute__ ((always_inline)) { _PORT::r() |= _MASK; }
- inline static void lo() __attribute__ ((always_inline)) { _PORT::r() &= ~_MASK; }
- inline static void set(register uint8_t val) __attribute__ ((always_inline)) { _PORT::r() = val; }
-
- inline static void strobe() __attribute__ ((always_inline)) { hi(); lo(); }
-
- 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 uint8_t val) __attribute__ ((always_inline)) { set(val); }
-
- inline static port_t hival() __attribute__ ((always_inline)) { return _PORT::r() | _MASK; }
- inline static port_t loval() __attribute__ ((always_inline)) { return _PORT::r() & ~_MASK; }
- inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PORT::r(); }
- inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
-};
-
-/// 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.
-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; }
+template<uint8_t PIN> RwReg FastPin<PIN>::sPinMask;
+template<uint8_t PIN> volatile RwReg *FastPin<PIN>::sPort;
+template<uint8_t PIN> volatile RoReg *FastPin<PIN>::sInPort;
- 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; }
+template<uint8_t PIN> class FastPinBB : public FastPin<PIN> {};
- 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_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
-/// 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 {
-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)) { *_PDOR::template rx<_BIT>() = 1; }
- inline static void lo() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 0; }
- 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)) { *port = 1; }
- inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port = 0; }
- 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 1; }
- inline static port_t loval() __attribute__ ((always_inline)) { return 0; }
- inline static port_ptr_t port() __attribute__ ((always_inline)) { return _PDOR::template rx<_BIT>(); }
- inline static port_t mask() __attribute__ ((always_inline)) { return 1; }
-};
-
-/// AVR definitions for pins. Getting around the fact that I can't pass GPIO register addresses in as template arguments by instead creating
-/// a custom type for each GPIO register with a single, static, aggressively inlined function that returns that specific GPIO register. A similar
-/// trick is used a bit further below for the ARM GPIO registers (of which there are far more than on AVR!)
-typedef volatile uint8_t & reg8_t;
-#define _R(T) struct __gen_struct_ ## T
-#define _RD8(T) struct __gen_struct_ ## T { static inline reg8_t r() { return T; }};
-#define _IO(L) _RD8(DDR ## L); _RD8(PORT ## L); _RD8(PIN ## L);
-#define _DEFPIN_AVR(PIN, MASK, L) template<> class FastPin<PIN> : public _AVRPIN<PIN, MASK, _R(PORT ## L), _R(DDR ## L), _R(PIN ## L)> {};
-
-// ARM definitions
-#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)))
typedef volatile uint32_t & reg32_t;
typedef volatile uint32_t * ptr_reg32_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);
-
-#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)> {};
-
-// Don't use bit band'd pins for now, the compiler generates far less efficient code around them
-// #define _DEFPIN_ARM(PIN, BIT, L) template<> class Pin<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)> {};
///////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -259,172 +138,22 @@ typedef volatile uint32_t * ptr_reg32_t;
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
#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
-
-#elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__)
-_IO(B);
-
-_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);
-
-#elif(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny25__)
-_IO(A); _IO(B);
-
-_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);
-
-#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
-// Accelerated port definitions for arduino avrs
-_IO(D); _IO(B); _IO(C);
-_DEFPIN_AVR( 0, 0x01, D); _DEFPIN_AVR( 1, 0x02, D); _DEFPIN_AVR( 2, 0x04, D); _DEFPIN_AVR( 3, 0x08, D);
-_DEFPIN_AVR( 4, 0x10, D); _DEFPIN_AVR( 5, 0x20, D); _DEFPIN_AVR( 6, 0x40, D); _DEFPIN_AVR( 7, 0x80, D);
-_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, 0x01, C); _DEFPIN_AVR(15, 0x02, C);
-_DEFPIN_AVR(16, 0x04, C); _DEFPIN_AVR(17, 0x08, C); _DEFPIN_AVR(18, 0x10, C); _DEFPIN_AVR(19, 0x20, C);
-
-#define SPI_DATA 11
-#define SPI_CLOCK 13
-#define SPI_SELECT 10
-#define AVR_HARDWARE_SPI
-
-#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
-// megas
-
-_IO(A); _IO(B); _IO(C); _IO(D); _IO(E); _IO(F); _IO(G); _IO(H); _IO(J); _IO(K); _IO(L);
-
-_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
-#define SPI_SELECT 53
-#define AVR_HARDWARE_SPI
-
-// Leonardo, teensy, blinkm
-#elif defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY)
-
-// teensy defs
-_IO(B); _IO(C); _IO(D); _IO(E); _IO(F);
-
-_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
-#define SPI_SELECT 3
-#define AVR_HARDWARE_SPI
-
-#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
-// teensy++ 2 defs
-
-_IO(A); _IO(B); _IO(C); _IO(D); _IO(E); _IO(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
-#define SPI_SELECT 20
-#define AVR_HARDWARE_SPI
-
-#elif defined(__AVR_ATmega32U4__)
-
-// leonard defs
-_IO(B); _IO(C); _IO(D); _IO(E); _IO(F);
-
-_DEFPIN_AVR(0, 4, D); _DEFPIN_AVR(1, 8, D); _DEFPIN_AVR(2, 2, D); _DEFPIN_AVR(3, 1, D);
-_DEFPIN_AVR(4, 16, D); _DEFPIN_AVR(5, 64, C); _DEFPIN_AVR(6, 128, D); _DEFPIN_AVR(7, 64, E);
-_DEFPIN_AVR(8, 16, B); _DEFPIN_AVR(9, 32, B); _DEFPIN_AVR(10, 64, B); _DEFPIN_AVR(11, 128, B);
-_DEFPIN_AVR(12, 64, D); _DEFPIN_AVR(13, 128, C); _DEFPIN_AVR(14, 8, B); _DEFPIN_AVR(15, 2, B);
-_DEFPIN_AVR(16, 4, B); _DEFPIN_AVR(17, 1, B); _DEFPIN_AVR(18, 128, F); _DEFPIN_AVR(19, 64, F);
-_DEFPIN_AVR(20, 32, F); _DEFPIN_AVR(21, 16, F); _DEFPIN_AVR(22, 2, F); _DEFPIN_AVR(23, 0, F);
-
-#define SPI_DATA 16
-#define SPI_CLOCK 15
-#define AVR_HARDWARE_SPI
-
-#elif defined(__MK20DX128__) && defined(CORE_TEENSY)
-
-_IO32(A); _IO32(B); _IO32(C); _IO32(D); _IO32(E);
-
-_DEFPIN_ARM(0, 16, B); _DEFPIN_ARM(1, 17, B); _DEFPIN_ARM(2, 0, D); _DEFPIN_ARM(3, 12, A);
-_DEFPIN_ARM(4, 13, 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, 5, A); _DEFPIN_ARM(25, 19, B); _DEFPIN_ARM(26, 1, E); _DEFPIN_ARM(27, 9, C);
-_DEFPIN_ARM(28, 8, C); _DEFPIN_ARM(29, 10, C); _DEFPIN_ARM(30, 11, C); _DEFPIN_ARM(31, 0, E);
-_DEFPIN_ARM(32, 18, B); _DEFPIN_ARM(33, 4, A);
-
-#define SPI_DATA 11
-#define SPI_CLOCK 13
-#define ARM_HARDWARE_SPI
-
-#elif defined(__SAM3X8E__)
-
-DUE_IO32(A);
-DUE_IO32(B);
-DUE_IO32(C);
-DUE_IO32(D);
-
-_DEFPIN_DUE(0, 8, A); _DEFPIN_DUE(1, 9, A); _DEFPIN_DUE(2, 25, B); _DEFPIN_DUE(3, 28, C);
-_DEFPIN_DUE(4, 26, C); _DEFPIN_DUE(5, 25, C); _DEFPIN_DUE(6, 24, C); _DEFPIN_DUE(7, 23, C);
-_DEFPIN_DUE(8, 22, C); _DEFPIN_DUE(9, 21, C); _DEFPIN_DUE(10, 29, C); _DEFPIN_DUE(11, 7, D);
-_DEFPIN_DUE(12, 8, D); _DEFPIN_DUE(13, 27, B); _DEFPIN_DUE(14, 4, D); _DEFPIN_DUE(15, 5, D);
-_DEFPIN_DUE(16, 13, A); _DEFPIN_DUE(17, 12, A); _DEFPIN_DUE(18, 11, A); _DEFPIN_DUE(19, 10, A);
-_DEFPIN_DUE(20, 12, B); _DEFPIN_DUE(21, 13, B); _DEFPIN_DUE(22, 26, B); _DEFPIN_DUE(23, 14, A);
-_DEFPIN_DUE(24, 15, A); _DEFPIN_DUE(25, 0, D); _DEFPIN_DUE(26, 1, D); _DEFPIN_DUE(27, 2, D);
-_DEFPIN_DUE(28, 3, D); _DEFPIN_DUE(29, 6, D); _DEFPIN_DUE(30, 9, D); _DEFPIN_DUE(31, 7, A);
-_DEFPIN_DUE(32, 10, D); _DEFPIN_DUE(33, 1, C); _DEFPIN_DUE(34, 2, C); _DEFPIN_DUE(35, 3, C);
-_DEFPIN_DUE(36, 4, C); _DEFPIN_DUE(37, 5, C); _DEFPIN_DUE(38, 6, C); _DEFPIN_DUE(39, 7, C);
-_DEFPIN_DUE(40, 8, C); _DEFPIN_DUE(41, 9, C); _DEFPIN_DUE(42, 19, A); _DEFPIN_DUE(43, 20, A);
-_DEFPIN_DUE(44, 19, C); _DEFPIN_DUE(45, 18, C); _DEFPIN_DUE(46, 17, C); _DEFPIN_DUE(47, 16, C);
-_DEFPIN_DUE(48, 15, C); _DEFPIN_DUE(49, 14, C); _DEFPIN_DUE(50, 13, C); _DEFPIN_DUE(51, 12, C);
-_DEFPIN_DUE(52, 21, B); _DEFPIN_DUE(53, 14, B); _DEFPIN_DUE(54, 16, A); _DEFPIN_DUE(55, 24, A);
-_DEFPIN_DUE(56, 23, A); _DEFPIN_DUE(57, 22, A); _DEFPIN_DUE(58, 6, A); _DEFPIN_DUE(59, 4, A);
-_DEFPIN_DUE(60, 3, A); _DEFPIN_DUE(61, 2, A); _DEFPIN_DUE(62, 17, B); _DEFPIN_DUE(63, 18, B);
-_DEFPIN_DUE(64, 19, B); _DEFPIN_DUE(65, 20, B); _DEFPIN_DUE(66, 15, B); _DEFPIN_DUE(67, 16, B);
-_DEFPIN_DUE(68, 1, A); _DEFPIN_DUE(69, 0, A); _DEFPIN_DUE(70, 17, A); _DEFPIN_DUE(71, 18, A);
-_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);
+#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
diff --git a/fastpin_arm_k20.h b/fastpin_arm_k20.h
new file mode 100644
index 00000000..71bddde3
--- /dev/null
+++ b/fastpin_arm_k20.h
@@ -0,0 +1,105 @@
+#ifndef __FASTPIN_ARM_K20_H
+#define __FASTPIN_ARM_K20_H
+
+/// 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 {
+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_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
+/// 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 {
+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)) { *_PDOR::template rx<_BIT>() = 1; }
+ inline static void lo() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 0; }
+ 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(); }
+ 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)) { *_PDOR::template rx<_BIT>() = val; }
+
+ inline static port_t hival() __attribute__ ((always_inline)) { return 1; }
+ inline static port_t loval() __attribute__ ((always_inline)) { return 0; }
+ inline static port_ptr_t port() __attribute__ ((always_inline)) { return _PDOR::template rx<_BIT>(); }
+ inline static port_t mask() __attribute__ ((always_inline)) { return 1; }
+};
+
+// Macros for k20 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 _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);
+
+#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)> {};
+
+// Actual pin definitions
+#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
+
+_IO32(A); _IO32(B); _IO32(C); _IO32(D); _IO32(E);
+
+#define MAX_PIN 33
+_DEFPIN_ARM(0, 16, B); _DEFPIN_ARM(1, 17, B); _DEFPIN_ARM(2, 0, D); _DEFPIN_ARM(3, 12, A);
+_DEFPIN_ARM(4, 13, 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, 5, A); _DEFPIN_ARM(25, 19, B); _DEFPIN_ARM(26, 1, E); _DEFPIN_ARM(27, 9, C);
+_DEFPIN_ARM(28, 8, C); _DEFPIN_ARM(29, 10, C); _DEFPIN_ARM(30, 11, C); _DEFPIN_ARM(31, 0, E);
+_DEFPIN_ARM(32, 18, B); _DEFPIN_ARM(33, 4, A);
+
+#define SPI_DATA 11
+#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
diff --git a/fastpin_arm_sam.h b/fastpin_arm_sam.h
new file mode 100644
index 00000000..164754c8
--- /dev/null
+++ b/fastpin_arm_sam.h
@@ -0,0 +1,107 @@
+#ifndef __INC_FASTPIN_ARM_SAM_H
+#define __INC_FASTPIN_ARM_SAM_H
+
+/// 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 {
+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)) { _PDOR::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_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
+/// 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 {
+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)) { *_PDOR::template rx<_BIT>() = 1; }
+ inline static void lo() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 0; }
+ 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(); }
+ 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 1; }
+ inline static port_t loval() __attribute__ ((always_inline)) { return 0; }
+ inline static port_ptr_t port() __attribute__ ((always_inline)) { return _PDOR::template rx<_BIT>(); }
+ inline static port_t mask() __attribute__ ((always_inline)) { return 1; }
+};
+
+#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)> {};
+
+#if defined(__SAM3X8E__)
+
+DUE_IO32(A);
+DUE_IO32(B);
+DUE_IO32(C);
+DUE_IO32(D);
+
+#define MAX_PIN 78
+_DEFPIN_DUE(0, 8, A); _DEFPIN_DUE(1, 9, A); _DEFPIN_DUE(2, 25, B); _DEFPIN_DUE(3, 28, C);
+_DEFPIN_DUE(4, 26, C); _DEFPIN_DUE(5, 25, C); _DEFPIN_DUE(6, 24, C); _DEFPIN_DUE(7, 23, C);
+_DEFPIN_DUE(8, 22, C); _DEFPIN_DUE(9, 21, C); _DEFPIN_DUE(10, 29, C); _DEFPIN_DUE(11, 7, D);
+_DEFPIN_DUE(12, 8, D); _DEFPIN_DUE(13, 27, B); _DEFPIN_DUE(14, 4, D); _DEFPIN_DUE(15, 5, D);
+_DEFPIN_DUE(16, 13, A); _DEFPIN_DUE(17, 12, A); _DEFPIN_DUE(18, 11, A); _DEFPIN_DUE(19, 10, A);
+_DEFPIN_DUE(20, 12, B); _DEFPIN_DUE(21, 13, B); _DEFPIN_DUE(22, 26, B); _DEFPIN_DUE(23, 14, A);
+_DEFPIN_DUE(24, 15, A); _DEFPIN_DUE(25, 0, D); _DEFPIN_DUE(26, 1, D); _DEFPIN_DUE(27, 2, D);
+_DEFPIN_DUE(28, 3, D); _DEFPIN_DUE(29, 6, D); _DEFPIN_DUE(30, 9, D); _DEFPIN_DUE(31, 7, A);
+_DEFPIN_DUE(32, 10, D); _DEFPIN_DUE(33, 1, C); _DEFPIN_DUE(34, 2, C); _DEFPIN_DUE(35, 3, C);
+_DEFPIN_DUE(36, 4, C); _DEFPIN_DUE(37, 5, C); _DEFPIN_DUE(38, 6, C); _DEFPIN_DUE(39, 7, C);
+_DEFPIN_DUE(40, 8, C); _DEFPIN_DUE(41, 9, C); _DEFPIN_DUE(42, 19, A); _DEFPIN_DUE(43, 20, A);
+_DEFPIN_DUE(44, 19, C); _DEFPIN_DUE(45, 18, C); _DEFPIN_DUE(46, 17, C); _DEFPIN_DUE(47, 16, C);
+_DEFPIN_DUE(48, 15, C); _DEFPIN_DUE(49, 14, C); _DEFPIN_DUE(50, 13, C); _DEFPIN_DUE(51, 12, C);
+_DEFPIN_DUE(52, 21, B); _DEFPIN_DUE(53, 14, B); _DEFPIN_DUE(54, 16, A); _DEFPIN_DUE(55, 24, A);
+_DEFPIN_DUE(56, 23, A); _DEFPIN_DUE(57, 22, A); _DEFPIN_DUE(58, 6, A); _DEFPIN_DUE(59, 4, A);
+_DEFPIN_DUE(60, 3, A); _DEFPIN_DUE(61, 2, A); _DEFPIN_DUE(62, 17, B); _DEFPIN_DUE(63, 18, B);
+_DEFPIN_DUE(64, 19, B); _DEFPIN_DUE(65, 20, B); _DEFPIN_DUE(66, 15, B); _DEFPIN_DUE(67, 16, B);
+_DEFPIN_DUE(68, 1, A); _DEFPIN_DUE(69, 0, A); _DEFPIN_DUE(70, 17, A); _DEFPIN_DUE(71, 18, A);
+_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);
+
+#define SPI_DATA 75
+#define SPI_CLOCK 76
+#define ARM_HARDWARE_SPI
+#define HAS_HARDWARE_PIN_SUPPORT
+
+#endif
+
+
+#endif
diff --git a/fastpin_avr.h b/fastpin_avr.h
new file mode 100644
index 00000000..103498c2
--- /dev/null
+++ b/fastpin_avr.h
@@ -0,0 +1,199 @@
+#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)
+#else
+#define AVR_PIN_CYCLES(_PIN) ((_PIN >= 24) ? 2 : 1)
+#endif
+
+
+/// 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 {
+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; }
+
+ inline static void hi() __attribute__ ((always_inline)) { _PORT::r() |= _MASK; }
+ inline static void lo() __attribute__ ((always_inline)) { _PORT::r() &= ~_MASK; }
+ 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(); }
+ inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
+ inline static void fastset(register port_ptr_t port, register uint8_t val) __attribute__ ((always_inline)) { set(val); }
+
+ inline static port_t hival() __attribute__ ((always_inline)) { return _PORT::r() | _MASK; }
+ inline static port_t loval() __attribute__ ((always_inline)) { return _PORT::r() & ~_MASK; }
+ inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PORT::r(); }
+ inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
+};
+
+
+
+/// AVR definitions for pins. Getting around the fact that I can't pass GPIO register addresses in as template arguments by instead creating
+/// a custom type for each GPIO register with a single, static, aggressively inlined function that returns that specific GPIO register. A similar
+/// trick is used a bit further below for the ARM GPIO registers (of which there are far more than on AVR!)
+typedef volatile uint8_t & reg8_t;
+#define _R(T) struct __gen_struct_ ## T
+#define _RD8(T) struct __gen_struct_ ## T { static inline reg8_t r() { return T; }};
+#define _IO(L) _RD8(DDR ## L); _RD8(PORT ## L); _RD8(PIN ## L);
+#define _DEFPIN_AVR(_PIN, MASK, L) template<> class FastPin<_PIN> : public _AVRPIN<_PIN, MASK, _R(PORT ## L), _R(DDR ## L), _R(PIN ## L)> {};
+
+#if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__)
+_IO(B);
+
+_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__)
+_IO(A); _IO(B);
+
+_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__)
+// Accelerated port definitions for arduino avrs
+_IO(D); _IO(B); _IO(C);
+
+#define MAX_PIN 19
+_DEFPIN_AVR( 0, 0x01, D); _DEFPIN_AVR( 1, 0x02, D); _DEFPIN_AVR( 2, 0x04, D); _DEFPIN_AVR( 3, 0x08, D);
+_DEFPIN_AVR( 4, 0x10, D); _DEFPIN_AVR( 5, 0x20, D); _DEFPIN_AVR( 6, 0x40, D); _DEFPIN_AVR( 7, 0x80, D);
+_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, 0x01, C); _DEFPIN_AVR(15, 0x02, C);
+_DEFPIN_AVR(16, 0x04, C); _DEFPIN_AVR(17, 0x08, C); _DEFPIN_AVR(18, 0x10, C); _DEFPIN_AVR(19, 0x20, C);
+
+#define SPI_DATA 11
+#define SPI_CLOCK 13
+#define SPI_SELECT 10
+#define AVR_HARDWARE_SPI 1
+#define HAS_HARDWARE_PIN_SUPPORT 1
+
+#elif defined(__AVR_ATmega1284P__)
+
+_IO(A); _IO(B); _IO(C); _IO(D);
+
+_DEFPIN_AVR(0, 1<<0, D); _DEFPIN_AVR(1, 1<<1, D); _DEFPIN_AVR(2, 1<<2, B); _DEFPIN_AVR(3, 1<<3, B);
+_DEFPIN_AVR(4, 1<<0, B); _DEFPIN_AVR(5, 1<<1, B); _DEFPIN_AVR(6, 1<<2, D); _DEFPIN_AVR(7, 1<<3, D);
+_DEFPIN_AVR(8, 1<<5, D); _DEFPIN_AVR(9, 1<<6, D); _DEFPIN_AVR(10, 1<<4, B); _DEFPIN_AVR(11, 1<<5, B);
+_DEFPIN_AVR(12, 1<<6, B); _DEFPIN_AVR(13, 1<<7, B); _DEFPIN_AVR(14, 1<<7, A); _DEFPIN_AVR(15, 1<<6, A);
+_DEFPIN_AVR(16, 1<<5, A); _DEFPIN_AVR(17, 1<<4, A); _DEFPIN_AVR(18, 1<<3, A); _DEFPIN_AVR(19, 1<<2, A);
+_DEFPIN_AVR(20, 1<<1, A); _DEFPIN_AVR(21, 1<<0, A); _DEFPIN_AVR(22, 1<<0, C); _DEFPIN_AVR(23, 1<<1, C);
+_DEFPIN_AVR(24, 1<<2, C); _DEFPIN_AVR(25, 1<<3, C); _DEFPIN_AVR(26, 1<<4, C); _DEFPIN_AVR(27, 1<<5, C);
+_DEFPIN_AVR(28, 1<<6, C); _DEFPIN_AVR(29, 1<<7, C); _DEFPIN_AVR(30, 1<<4, D); _DEFPIN_AVR(31, 1<<7, D);
+
+#define SPI_DATA 11
+#define SPI_CLOCK 13
+#define SPI_SELECT 10
+#define AVR_HARDWARE_SPI 1
+#define HAS_HARDWARE_PIN_SUPPORT 1
+
+#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+// megas
+
+_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);
+
+#define SPI_DATA 51
+#define SPI_CLOCK 52
+#define SPI_SELECT 53
+#define AVR_HARDWARE_SPI 1
+#define HAS_HARDWARE_PIN_SUPPORT 1
+
+// Leonardo, teensy, blinkm
+#elif defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY)
+
+// teensy defs
+_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);
+
+#define SPI_DATA 2
+#define SPI_CLOCK 1
+#define SPI_SELECT 0
+#define AVR_HARDWARE_SPI 1
+#define HAS_HARDWARE_PIN_SUPPORT 1
+
+#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
+// teensy++ 2 defs
+
+_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);
+
+#define SPI_DATA 22
+#define SPI_CLOCK 21
+#define SPI_SELECT 20
+#define AVR_HARDWARE_SPI 1
+#define HAS_HARDWARE_PIN_SUPPORT 1
+
+#elif defined(__AVR_ATmega32U4__)
+
+// leonard defs
+_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);
+_DEFPIN_AVR(4, 16, D); _DEFPIN_AVR(5, 64, C); _DEFPIN_AVR(6, 128, D); _DEFPIN_AVR(7, 64, E);
+_DEFPIN_AVR(8, 16, B); _DEFPIN_AVR(9, 32, B); _DEFPIN_AVR(10, 64, B); _DEFPIN_AVR(11, 128, B);
+_DEFPIN_AVR(12, 64, D); _DEFPIN_AVR(13, 128, C); _DEFPIN_AVR(14, 8, B); _DEFPIN_AVR(15, 2, B);
+_DEFPIN_AVR(16, 4, B); _DEFPIN_AVR(17, 1, B); _DEFPIN_AVR(18, 128, F); _DEFPIN_AVR(19, 64, F);
+_DEFPIN_AVR(20, 32, F); _DEFPIN_AVR(21, 16, F); _DEFPIN_AVR(22, 2, F); _DEFPIN_AVR(23, 0, F);
+
+#define SPI_DATA 16
+#define SPI_CLOCK 15
+#define AVR_HARDWARE_SPI 1
+#define HAS_HARDWARE_PIN_SUPPORT 1
+
+#endif
+
+#endif
diff --git a/fastspi.h b/fastspi.h
index 00747137..f80956f4 100644
--- a/fastspi.h
+++ b/fastspi.h
@@ -17,17 +17,17 @@
/// byte worked on. Recommendation, make the adjust method aggressively inlined.
///
/// TODO: Convinience macro for building these
-class DATA_NOP {
+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 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
+// Clock speed dividers
#define SPEED_DIV_2 2
#define SPEED_DIV_4 4
#define SPEED_DIV_8 8
@@ -37,12 +37,18 @@ public:
#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)
+#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.h"
+#include "fastspi_arm_k20.h"
+#include "fastspi_arm_sam.h"
#include "fastspi_avr.h"
#include "fastspi_dma.h"
@@ -62,10 +68,21 @@ class SoftwareSPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SP
#ifndef FORCE_SOFTWARE_SPI
#if defined(SPI_DATA) && defined(SPI_CLOCK)
-#if defined(__MK20DX128__) && defined(CORE_TEENSY)
+#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
+
+template<uint8_t SPI_SPEED>
+class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED, 0x4002C000> {};
+
+#if defined(SPI2_DATA)
template<uint8_t SPI_SPEED>
-class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> {};
+class SPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED, 0x4002C000> {};
+#endif
+
+#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
@@ -86,6 +103,6 @@ class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public AVRHardwareSPIOutput<SP
#else
#warning "Forcing software SPI - no hardware SPI for you!"
-#endif
+#endif
#endif
diff --git a/fastspi_arm.h b/fastspi_arm_k20.h
index e9c38343..ad9598ae 100644
--- a/fastspi_arm.h
+++ b/fastspi_arm_k20.h
@@ -2,26 +2,31 @@
#define __INC_FASTSPI_ARM_H
-#if defined(__MK20DX128__) && defined(CORE_TEENSY)
+#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
+
+// Version 1.20 renamed SPI_t to KINETISK_SPI_t
+#if TEENSYDUINO >= 120
+#define SPI_t KINETISK_SPI_t
+#endif
#ifndef SPI_PUSHR_CONT
-#define SPI_PUSHR_CONT SPI0_PUSHR_CONT
-#define SPI_PUSHR_CTAS(X) SPI0_PUSHR_CTAS(X)
-#define SPI_PUSHR_EOQ SPI0_PUSHR_EOQ
-#define SPI_PUSHR_CTCNT SPI0_PUSHR_CTCNT
-#define SPI_PUSHR_PCS(X) SPI0_PUSHR_PCS(X)
+#define SPI_PUSHR_CONT SPIX.PUSHR_CONT
+#define SPI_PUSHR_CTAS(X) SPIX.PUSHR_CTAS(X)
+#define SPI_PUSHR_EOQ SPIX.PUSHR_EOQ
+#define SPI_PUSHR_CTCNT SPIX.PUSHR_CTCNT
+#define SPI_PUSHR_PCS(X) SPIX.PUSHR_PCS(X)
#endif
-// Template function that, on compilation, expands to a constant representing the highest bit set in a byte. Right now,
+// Template function that, on compilation, expands to a constant representing the highest bit set in a byte. Right now,
// 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(); }
+template<int VAL, int BIT> class BitWork {
+ 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; }
+template<int VAL> class BitWork<VAL, 0> {
+ public:
+ static int highestBit() __attribute__((always_inline)) { return 0; }
};
#define MAX(A, B) (( (A) > (B) ) ? (A) : (B))
@@ -74,82 +79,71 @@ template <int VAL> void getScalars(uint32_t & preScalar, uint32_t & scalar, uint
return;
}
+#define SPIX (*(SPI_t*)pSPIX)
-template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
-class ARMHardwareSPIOutput {
+template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX>
+class ARMHardwareSPIOutput {
Selectable *m_pSelect;
// Borrowed from the teensy3 SPSR emulation code
static inline void enable_pins(void) __attribute__((always_inline)) {
//serial_print("enable_pins\n");
- 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);
+ 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);
+ }
}
// Borrowed from the teensy3 SPSR emulation code
static inline void disable_pins(void) __attribute__((always_inline)) {
//serial_print("disable_pins\n");
- 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);
- }
-
+ 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);
+ }
+}
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 (SPI0_CTAR0 == ctar) return;
- uint32_t mcr = SPI0_MCR;
+ if (SPIX.CTAR0 == ctar) return;
+ uint32_t mcr = SPIX.MCR;
if (mcr & SPI_MCR_MDIS) {
- SPI0_CTAR0 = ctar;
+ SPIX.CTAR0 = ctar;
} else {
- SPI0_MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
- SPI0_CTAR0 = ctar;
+ SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
+ SPIX.CTAR0 = ctar;
- SPI0_MCR = mcr;
+ SPIX.MCR = mcr;
}
- }
+ }
static inline void update_ctar1(uint32_t ctar) __attribute__((always_inline)) {
- if (SPI0_CTAR1 == ctar) return;
- uint32_t mcr = SPI0_MCR;
+ if (SPIX.CTAR1 == ctar) return;
+ uint32_t mcr = SPIX.MCR;
if (mcr & SPI_MCR_MDIS) {
- SPI0_CTAR1 = ctar;
+ SPIX.CTAR1 = ctar;
} else {
- SPI0_MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
- SPI0_CTAR1 = ctar;
- SPI0_MCR = mcr;
-
- }
- }
-
- static inline void set_ctar1_bits(int bits) {
- // Set ctar1 to 16 bits
- int ctar = SPI0_CTAR1;
-
- // clear the FMSZ bits
- ctar &= SPI_CTAR_FMSZ(0x0F);
- ctar |= SPI_CTAR_FMSZ((bits-1) & 0x0F);
-
- update_ctar1(ctar);
- }
-
- static inline void set_ctar0_bits(int bits) {
- // Set ctar1 to 16 bits
- int ctar = SPI0_CTAR1;
-
- // clear the FMSZ bits
- ctar &= SPI_CTAR_FMSZ(0x0F);
- ctar |= SPI_CTAR_FMSZ((bits-1) & 0x0F);
+ SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
+ SPIX.CTAR1 = ctar;
+ SPIX.MCR = mcr;
- update_ctar1(ctar);
+ }
}
-
- void setSPIRate() {
+ void setSPIRate() {
// Configure CTAR0, defaulting to 8 bits and CTAR1, defaulting to 16 bits
uint32_t _PBR = 0;
uint32_t _BR = 0;
@@ -179,7 +173,7 @@ public:
ctar1 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
#endif
- if(_DBR) {
+ if(_DBR) {
ctar0 |= SPI_CTAR_DBR;
ctar1 |= SPI_CTAR_DBR;
}
@@ -188,7 +182,7 @@ public:
update_ctar1(ctar1);
}
-
+
void init() {
// set the pins to output
FastPin<_DATA_PIN>::setOutput();
@@ -197,31 +191,38 @@ public:
// Enable SPI0 clock
uint32_t sim6 = SIM_SCGC6;
- if (!(sim6 & SIM_SCGC6_SPI0)) {
- //serial_print("init1\n");
- SIM_SCGC6 = sim6 | SIM_SCGC6_SPI0;
- SPI0_CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1);
+ if((SPI_t*)pSPIX == &SPI0) {
+ if (!(sim6 & SIM_SCGC6_SPI0)) {
+ //serial_print("init1\n");
+ SIM_SCGC6 = sim6 | SIM_SCGC6_SPI0;
+ SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1);
+ }
+ } else if((SPI_t*)pSPIX == &SPI1) {
+ if (!(sim6 & SIM_SCGC6_SPI1)) {
+ //serial_print("init1\n");
+ SIM_SCGC6 = sim6 | SIM_SCGC6_SPI1;
+ SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1);
+ }
}
-
setSPIRate();
- // Configure SPI as the master and enable
- SPI0_MCR |= SPI_MCR_MSTR; // | SPI_MCR_CONT_SCKE);
- SPI0_MCR &= ~(SPI_MCR_MDIS | SPI_MCR_HALT);
+ // 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();
}
- static void waitFully() __attribute__((always_inline)) {
- while( (SPI0_SR & 0xF000) > 0);
- while (!(SPI0_SR & SPI_SR_TCF));
- SPI0_SR |= (SPI_SR_TCF | SPI_SR_EOQF);
+ static void waitFully() __attribute__((always_inline)) {
+ while( (SPIX.SR & 0xF000) > 0);
+ while (!(SPIX.SR & SPI_SR_TCF));
+ SPIX.SR |= (SPI_SR_TCF | SPI_SR_EOQF);
}
- static bool needwait() __attribute__((always_inline)) { return (SPI0_SR & 0x4000); }
- static void wait() __attribute__((always_inline)) { while( (SPI0_SR & 0x4000) ); }
- static void wait1() __attribute__((always_inline)) { while( (SPI0_SR & 0xF000) >= 0x2000); }
-
+ static bool needwait() __attribute__((always_inline)) { return (SPIX.SR & 0x4000); }
+ static void wait() __attribute__((always_inline)) { while( (SPIX.SR & 0x4000) ); }
+ static void wait1() __attribute__((always_inline)) { while( (SPIX.SR & 0xF000) >= 0x2000); }
+
enum ECont { CONT, NOCONT };
enum EWait { PRE, POST, NONE };
enum ELast { NOTLAST, LAST };
@@ -233,44 +234,44 @@ public:
#endif
#define WM PRE
- template<ECont CONT_STATE, EWait WAIT_STATE, ELast LAST_STATE> class Write {
+ template<ECont CONT_STATE, EWait WAIT_STATE, ELast LAST_STATE> class Write {
public:
- static void writeWord(uint16_t w) __attribute__((always_inline)) {
+ static void writeWord(uint16_t w) __attribute__((always_inline)) {
if(WAIT_STATE == PRE) { wait(); }
- SPI0_PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
- ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
+ SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
+ ((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)) {
+ static void writeByte(uint8_t b) __attribute__((always_inline)) {
if(WAIT_STATE == PRE) { wait(); }
- SPI0_PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
- ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
+ SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
+ ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
SPI_PUSHR_CTAS(0) | (b & 0xFF);
if(WAIT_STATE == POST) { wait(); }
}
};
- static void writeWord(uint16_t w) __attribute__((always_inline)) { wait(); SPI0_PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); }
- static void writeWordNoWait(uint16_t w) __attribute__((always_inline)) { SPI0_PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); }
+ static void writeWord(uint16_t w) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); }
+ static void writeWordNoWait(uint16_t w) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); }
- static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPI0_PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); }
- static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPI0_PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); wait(); }
- static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPI0_PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); }
+ static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); }
+ static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); wait(); }
+ static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); }
- static void writeWordCont(uint16_t w) __attribute__((always_inline)) { wait(); SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); }
- static void writeWordContNoWait(uint16_t w) __attribute__((always_inline)) { SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); }
+ static void writeWordCont(uint16_t w) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); }
+ static void writeWordContNoWait(uint16_t w) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); }
- static void writeByteCont(uint8_t b) __attribute__((always_inline)) { wait(); SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); }
- static void writeByteContPostWait(uint8_t b) __attribute__((always_inline)) { SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); wait(); }
- static void writeByteContNoWait(uint8_t b) __attribute__((always_inline)) { SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); }
+ static void writeByteCont(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); }
+ static void writeByteContPostWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); wait(); }
+ static void writeByteContNoWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); }
// 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) {
- uint32_t ctar1_save = SPI0_CTAR1;
+ template <uint8_t BIT> inline static void writeBit(uint8_t b) {
+ uint32_t ctar1_save = SPIX.CTAR1;
- // Clear out the FMSZ bits, reset them for 9 bits transferd for the start bit
+ // Clear out the FMSZ bits, reset them for 1 bit transferd for the start bit
uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(0);
update_ctar1(ctar1);
@@ -279,107 +280,95 @@ 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)) { if(m_pSelect != NULL) { m_pSelect->select(); } }
+ void inline release() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->release(); } }
static void writeBytesValueRaw(uint8_t value, int len) {
while(len--) { Write<CM, WM, NOTLAST>::writeByte(value); }
}
- void writeBytesValue(uint8_t value, int len) {
+ void writeBytesValue(uint8_t value, int len) {
setSPIRate();
select();
- while(len--) {
+ while(len--) {
writeByte(value);
}
waitFully();
release();
}
-
- // Write a block of n uint8_ts out
- template <class D> void writeBytes(register uint8_t *data, int len) {
+
+ // 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) {
+ // could be optimized to write 16bit words out instead of 8bit bytes
+ while(data != end) {
writeByte(D::adjust(*data++));
}
D::postBlock(len);
waitFully();
- release();
+ 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 SKIP, class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+ template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
// setSPIRate();
- uint8_t *end = data + len;
select();
- if((SKIP & FLAG_START_BIT) == 0) {
+ int len = pixels.mLen;
+
+ // Setup the pixel controller
+ if((FLAGS & FLAG_START_BIT) == 0) {
//If no start bit stupiditiy, write out as many 16-bit blocks as we can
- uint8_t *first_end = end - (len % (SPI_ADVANCE * 2));
-
- while(data != first_end) {
+ while(pixels.has(2)) {
+ // Load and write out the first two bytes
if(WM == NONE) { wait1(); }
- Write<CM, WM, NOTLAST>::writeWord(D::adjust(data[SPI_B0], scale) << 8 | D::adjust(data[SPI_B1], scale));
- Write<CM, WM, NOTLAST>::writeWord(D::adjust(data[SPI_B2], scale) << 8 | D::adjust(data[SPI_ADVANCE + SPI_B0], scale));
- Write<CM, WM, NOTLAST>::writeWord(D::adjust(data[SPI_ADVANCE + SPI_B1], scale) << 8 | D::adjust(data[SPI_ADVANCE + SPI_B2], scale));
- data += (SPI_ADVANCE + SPI_ADVANCE);
+ Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1()));
+
+ // Load and write out the next two bytes (step dithering, advance data in between since we
+ // cross pixels here)
+ Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale2()) << 8 | D::adjust(pixels.stepAdvanceAndLoadAndScale0()));
+
+ // Load and write out the next two bytes
+ Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale1()) << 8 | D::adjust(pixels.loadAndScale2()));
+ pixels.stepDithering();
+ pixels.advanceData();
}
- if(data != end) {
+ if(pixels.has(1)) {
if(WM == NONE) { wait1(); }
// write out the rest as alternating 16/8-bit blocks (likely to be just one)
- Write<CM, WM, NOTLAST>::writeWord(D::adjust(data[SPI_B0], scale) << 8 | D::adjust(data[SPI_B1], scale));
- Write<CM, WM, NOTLAST>::writeByte(D::adjust(data[SPI_B2], scale));
+ Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1()));
+ Write<CM, WM, NOTLAST>::writeByte(D::adjust(pixels.loadAndScale2()));
}
D::postBlock(len);
waitFully();
- } else if(SKIP & FLAG_START_BIT) {
- uint32_t ctar1_save = SPI0_CTAR1;
+ } else if(FLAGS & FLAG_START_BIT) {
+ uint32_t ctar1_save = SPIX.CTAR1;
// Clear out the FMSZ bits, reset them for 9 bits transferd for the start bit
uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(8);
update_ctar1(ctar1);
- while(data != end) {
- writeWord( 0x100 | D::adjust(data[SPI_B0], scale));
- writeByte(D::adjust(data[SPI_B1], scale));
- writeByte(D::adjust(data[SPI_B2], scale));
- data += SPI_ADVANCE;
+ while(pixels.has(1)) {
+ writeWord( 0x100 | D::adjust(pixels.loadAndScale0()));
+ writeByte(D::adjust(pixels.loadAndScale1()));
+ writeByte(D::adjust(pixels.loadAndScale2()));
+ pixels.advanceData();
+ pixels.stepDithering();
}
D::postBlock(len);
waitFully();
// restore ctar1
update_ctar1(ctar1_save);
- // } else {
- // while(data != end) {
- // writeByte(D::adjust(data[SPI_B0], scale);
- // writeWord(D::adjust(data[SPI_B1], scale) << 8 | D::adjust(data[SPI_B2], scale));
- // data += SPI_ADVANCE;
- // }
- // waitFully();
}
release();
}
-
-
- 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);
- }
};
#endif
diff --git a/fastspi_arm_sam.h b/fastspi_arm_sam.h
new file mode 100644
index 00000000..d756c221
--- /dev/null
+++ b/fastspi_arm_sam.h
@@ -0,0 +1,160 @@
+#ifndef __INC_FASTSPI_ARM_SAM_H
+#define __INC_FASTSPI_ARM_SAM_H
+
+#if defined(__SAM3X8E__)
+#define m_SPI ((Spi*)SPI0)
+
+template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
+class SAMHardwareSPIOutput {
+ Selectable *m_pSelect;
+
+ static inline void waitForEmpty() { while ((m_SPI->SPI_SR & SPI_SR_TDRE) == 0); }
+
+ void enableConfig() { m_SPI->SPI_WPMR &= ~SPI_WPMR_WPEN; }
+ void disableConfig() { m_SPI->SPI_WPMR |= SPI_WPMR_WPEN; }
+
+ void enableSPI() { m_SPI->SPI_CR = SPI_CR_SPIEN; }
+ void disableSPI() { m_SPI->SPI_CR = SPI_CR_SPIDIS; }
+ void resetSPI() { m_SPI->SPI_CR = SPI_CR_SWRST; }
+
+ static inline void readyTransferBits(register uint32_t bits) {
+ bits -= 8;
+ // don't change the number of transfer bits while data is still being transferred from TDR to the shift register
+ waitForEmpty();
+ m_SPI->SPI_CSR[0] = SPI_CSR_NCPHA | SPI_CSR_CSAAT | (bits << SPI_CSR_BITS_Pos) | SPI_CSR_DLYBCT(1) | SPI_CSR_SCBR(_SPI_CLOCK_DIVIDER);
+ }
+
+ template<int BITS> static inline void writeBits(uint16_t w) {
+ waitForEmpty();
+ m_SPI->SPI_TDR = (uint32_t)w | SPI_PCS(0);
+ }
+
+public:
+ SAMHardwareSPIOutput() { m_pSelect = NULL; }
+ SAMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
+
+ // set the object representing the selectable
+ void setSelect(Selectable *pSelect) { /* TODO */ }
+
+ // initialize the SPI subssytem
+ void init() {
+ // m_SPI = SPI0;
+
+ // set the output pins master out, master in, clock. Note doing this here because I still don't
+ // know how I want to expose this type of functionality in FastPin.
+ PIO_Configure(PIOA, PIO_PERIPH_A, FastPin<_DATA_PIN>::mask(), PIO_DEFAULT);
+ PIO_Configure(PIOA, PIO_PERIPH_A, FastPin<_DATA_PIN-1>::mask(), PIO_DEFAULT);
+ PIO_Configure(PIOA, PIO_PERIPH_A, FastPin<_CLOCK_PIN>::mask(), PIO_DEFAULT);
+
+ release();
+
+ // Configure the SPI clock, divider between 1-255
+ // SCBR = _SPI_CLOCK_DIVIDER
+ pmc_enable_periph_clk(ID_SPI0);
+ disableSPI();
+
+ // reset twice (what the sam code does, not sure why?)
+ resetSPI();
+ resetSPI();
+
+ // Configure SPI as master, enable
+ // Bits we want in MR: master, disable mode fault detection, variable peripheral select
+ m_SPI->SPI_MR = SPI_MR_MSTR | SPI_MR_MODFDIS | SPI_MR_PS;
+
+ enableSPI();
+
+ // Send everything out in 8 bit chunks, other sizes appear to work, poorly...
+ readyTransferBits(8);
+ }
+
+ // latch the CS 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(); } }
+
+ // wait until all queued up data has been written
+ void waitFully() { while((m_SPI->SPI_SR & SPI_SR_TXEMPTY) == 0); }
+
+ // write a byte out via SPI (returns immediately on writing register)
+ static void writeByte(uint8_t b) {
+ writeBits<8>(b);
+ }
+
+ // write a word out via SPI (returns immediately on writing register)
+ static void writeWord(uint16_t w) {
+ writeBits<16>(w);
+ }
+
+ // 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) {
+ select(); writeBytesValueRaw(value, len); release();
+ }
+
+ 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) {
+ writeByte(D::adjust(*data++));
+ }
+ D::postBlock(len);
+ waitFully();
+ release();
+ }
+
+ void writeBytes(register 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
+ // not the most efficient mechanism in the world - but should be enough for sm16716 and friends
+ template <uint8_t BIT> inline void writeBit(uint8_t b) {
+ // need to wait for all exisiting data to go out the door, first
+ waitFully();
+ disableSPI();
+ if(b & (1 << BIT)) {
+ FastPin<_DATA_PIN>::hi();
+ } else {
+ FastPin<_DATA_PIN>::lo();
+ }
+
+ FastPin<_CLOCK_PIN>::hi();
+ FastPin<_CLOCK_PIN>::lo();
+ enableSPI();
+ }
+
+ // 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) {
+ select();
+ int len = pixels.mLen;
+
+ if(FLAGS & FLAG_START_BIT) {
+ while(pixels.has(1)) {
+ writeBits<9>((1<<8) | D::adjust(pixels.loadAndScale0()));
+ writeByte(D::adjust(pixels.loadAndScale1()));
+ writeByte(D::adjust(pixels.loadAndScale2()));
+ pixels.advanceData();
+ pixels.stepDithering();
+ }
+ } else {
+ while(pixels.has(1)) {
+ writeByte(D::adjust(pixels.loadAndScale0()));
+ writeByte(D::adjust(pixels.loadAndScale1()));
+ writeByte(D::adjust(pixels.loadAndScale2()));
+ pixels.advanceData();
+ pixels.stepDithering();
+ }
+ }
+ D::postBlock(len);
+ release();
+ }
+};
+
+#endif
+
+#endif
diff --git a/fastspi_avr.h b/fastspi_avr.h
index af116cab..6470f37d 100644
--- a/fastspi_avr.h
+++ b/fastspi_avr.h
@@ -13,7 +13,7 @@
#if defined(AVR_HARDWARE_SPI)
#if defined(UBRR0)
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
-class AVRUSARTSPIOutput {
+class AVRUSARTSPIOutput {
Selectable *m_pSelect;
public:
@@ -21,7 +21,7 @@ public:
AVRUSARTSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
- void init() {
+ void init() {
UBRR0 = 0;
UCSR0A = 1<<TXC0;
@@ -35,23 +35,23 @@ public:
UBRR0 = 3; // 2 Mhz clock rate
}
- static void stop() {
+ static void stop() {
// TODO: stop the uart spi output
}
static void wait() __attribute__((always_inline)) { while(!(UCSR0A & (1<<UDRE0))); }
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); }
-
- template <uint8_t BIT> inline static void writeBit(uint8_t b) {
- if(b && (1 << BIT)) {
+
+ template <uint8_t BIT> inline static void writeBit(uint8_t b) {
+ if(b && (1 << BIT)) {
FastPin<_DATA_PIN>::hi();
- } else {
+ } else {
FastPin<_DATA_PIN>::lo();
}
@@ -60,68 +60,71 @@ public:
}
void select() { if(m_pSelect != NULL) { m_pSelect->select(); } } // FastPin<_SELECT_PIN>::hi(); }
- void release() {
+ 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(); } // FastPin<_SELECT_PIN>::hi();
}
static void writeBytesValueRaw(uint8_t value, int len) {
while(len--) { writeByte(value); }
}
-
- void writeBytesValue(uint8_t value, int len) {
+
+ void writeBytesValue(uint8_t value, int len) {
select();
- while(len--) {
+ while(len--) {
writeByte(value);
}
release();
}
-
- // Write a block of n uint8_ts out
- template <class D> void writeBytes(register uint8_t *data, int len) {
+
+ // Write a block of n uint8_ts out
+ template <class D> void writeBytes(register uint8_t *data, int len) {
uint8_t *end = data + len;
select();
- while(data != end) {
-#if defined(__MK20DX128__)
- writeByte(D::adjust(*data++));
-#else
+ 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>();
-#endif
}
D::postBlock(len);
- release();
+ 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 SKIP, class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+ 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);
select();
- while(data != end) {
- writeByte(D::adjust(data[SPI_B0], scale));
- writeByte(D::adjust(data[SPI_B1], scale));
- writeByte(D::adjust(data[SPI_B2], scale));
- data += SPI_ADVANCE;
+ while(data != end) {
+ 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();
+ data += (3+skip);
}
D::postBlock(len);
release();
}
- 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 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 <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 <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);
}
- 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);
+ 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 writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
- writeBytes3<0, DATA_NOP, RGB>(data, len, scale);
+ 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);
}
};
@@ -135,14 +138,14 @@ public:
// Hardware SPI support using SPDR registers and friends
//
// Technically speaking, this uses the AVR SPI registers. This will work on the Teensy 3.0 because Paul made a set of compatability
-// classes that map the AVR SPI registers to ARM's, however this caps the performance of output.
+// classes that map the AVR SPI registers to ARM's, however this caps the performance of output.
//
// TODO: implement ARMHardwareSPIOutput
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
-class AVRHardwareSPIOutput {
+class AVRHardwareSPIOutput {
Selectable *m_pSelect;
bool mWait;
public:
@@ -150,7 +153,7 @@ public:
AVRHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
- void setSPIRate() {
+ void setSPIRate() {
SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear out the prescalar bits
bool b2x = false;
@@ -158,7 +161,7 @@ public:
if(_SPI_CLOCK_DIVIDER >= 128) { SPCR |= (1<<SPR1); SPCR |= (1<<SPR0); }
else if(_SPI_CLOCK_DIVIDER >= 64) { SPCR |= (1<<SPR1);}
else if(_SPI_CLOCK_DIVIDER >= 32) { SPCR |= (1<<SPR1); b2x = true; }
- else if(_SPI_CLOCK_DIVIDER >= 16) { SPCR |= (1<<SPR0); }
+ else if(_SPI_CLOCK_DIVIDER >= 16) { SPCR |= (1<<SPR0); }
else if(_SPI_CLOCK_DIVIDER >= 8) { SPCR |= (1<<SPR0); b2x = true; }
else if(_SPI_CLOCK_DIVIDER >= 4) { /* do nothing - default rate */ }
else { b2x = true; }
@@ -166,7 +169,7 @@ public:
if(b2x) { SPSR |= (1<<SPI2X); }
else { SPSR &= ~ (1<<SPI2X); }
}
-
+
void init() {
volatile uint8_t clr;
@@ -183,16 +186,16 @@ public:
SPCR |= ((1<<SPE) | (1<<MSTR) ); // enable SPI as master
SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear out the prescalar bits
- clr = SPSR; // clear SPI status register
+ clr = SPSR; // clear SPI status register
clr = SPDR; // clear SPI data register
- clr;
+ clr;
bool b2x = false;
if(_SPI_CLOCK_DIVIDER >= 128) { SPCR |= (1<<SPR1); SPCR |= (1<<SPR0); }
else if(_SPI_CLOCK_DIVIDER >= 64) { SPCR |= (1<<SPR1);}
else if(_SPI_CLOCK_DIVIDER >= 32) { SPCR |= (1<<SPR1); b2x = true; }
- else if(_SPI_CLOCK_DIVIDER >= 16) { SPCR |= (1<<SPR0); }
+ else if(_SPI_CLOCK_DIVIDER >= 16) { SPCR |= (1<<SPR0); }
else if(_SPI_CLOCK_DIVIDER >= 8) { SPCR |= (1<<SPR0); b2x = true; }
else if(_SPI_CLOCK_DIVIDER >= 4) { /* do nothing - default rate */ }
else { b2x = true; }
@@ -204,23 +207,25 @@ public:
shouldWait(false);
}
- static bool shouldWait(bool wait = false) __attribute__((always_inline)) {
- static bool sWait=false;
- if(sWait) { sWait = wait; return true; } else { sWait = wait; return false; }
+ 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(!(SPSR & (1<<SPIF))); } }
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(); SPDR=b; shouldWait(true); }
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPDR=b; shouldWait(true); wait(); }
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPDR=b; shouldWait(true); }
- template <uint8_t BIT> inline static void writeBit(uint8_t b) {
+ template <uint8_t BIT> inline static void writeBit(uint8_t b) {
SPCR &= ~(1 << SPE);
- if(b & (1 << BIT)) {
+ if(b & (1 << BIT)) {
FastPin<_DATA_PIN>::hi();
- } else {
+ } else {
FastPin<_DATA_PIN>::lo();
}
@@ -237,73 +242,54 @@ public:
while(len--) { writeByte(value); }
}
- void writeBytesValue(uint8_t value, int len) {
+ void writeBytesValue(uint8_t value, int len) {
//setSPIRate();
select();
- while(len--) {
+ while(len--) {
writeByte(value);
}
release();
}
-
- // Write a block of n uint8_ts out
- template <class D> void writeBytes(register uint8_t *data, int len) {
+
+ // 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) {
+ 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();
+ 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 SKIP, class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+ template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
//setSPIRate();
- uint8_t *end = data + len;
+ int len = pixels.mLen;
+
select();
- while(data != end) {
- if(SKIP & FLAG_START_BIT) {
+ while(pixels.has(1)) {
+ if(FLAGS & FLAG_START_BIT) {
writeBit<0>(1);
- }
- // a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM)
- if(false && _SPI_CLOCK_DIVIDER == 0) {
- writeByteNoWait(D::adjust(data[SPI_B0], scale)); delaycycles<13>();
- writeByteNoWait(D::adjust(data[SPI_B1], scale)); delaycycles<13>();
- writeByteNoWait(D::adjust(data[SPI_B2], scale)); delaycycles<9>();
- } else if(SKIP & FLAG_START_BIT) {
- writeBytePostWait(D::adjust(data[SPI_B0], scale));
- writeBytePostWait(D::adjust(data[SPI_B1], scale));
- writeBytePostWait(D::adjust(data[SPI_B2], scale));
- } else {
- writeByte(D::adjust(data[SPI_B0], scale));
- writeByte(D::adjust(data[SPI_B1], scale));
- writeByte(D::adjust(data[SPI_B2], scale));
+ 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()));
}
- data += SPI_ADVANCE;
+ pixels.advanceData();
+ pixels.stepDithering();
}
D::postBlock(len);
release();
}
-
- 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);
- }
-
};
#endif
@@ -311,4 +297,4 @@ public:
// #define FORCE_SOFTWARE_SPI
#endif
-#endif \ No newline at end of file
+#endif
diff --git a/fastspi_bitbang.h b/fastspi_bitbang.h
index f9c1a218..b596f628 100644
--- a/fastspi_bitbang.h
+++ b/fastspi_bitbang.h
@@ -10,7 +10,7 @@
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint8_t SPI_SPEED>
-class AVRSoftwareSPIOutput {
+class AVRSoftwareSPIOutput {
// The data types for pointers to the pin port - typedef'd here from the Pin definition because on avr these
// are pointers to 8 bit values, while on arm they are 32 bit
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
@@ -41,14 +41,14 @@ public:
// wait until the SPI subsystem is ready for more data to write. A NOP when bitbanging
static void wait() __attribute__((always_inline)) { }
static void waitFully() __attribute__((always_inline)) { wait(); }
-
+
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); }
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); wait(); }
static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
-
+
// naive writeByte implelentation, simply calls writeBit on the 8 bits in the byte.
- static void writeByte(uint8_t b) __attribute__((always_inline)) {
+ static void writeByte(uint8_t b) {
writeBit<7>(b);
writeBit<6>(b);
writeBit<5>(b);
@@ -59,9 +59,9 @@ public:
writeBit<0>(b);
}
-private:
+private:
// writeByte implementation with data/clock registers passed in.
- static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) __attribute__((always_inline)) {
+ static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
writeBit<7>(b, clockpin, datapin);
writeBit<6>(b, clockpin, datapin);
writeBit<5>(b, clockpin, datapin);
@@ -73,11 +73,11 @@ private:
}
// writeByte implementation with the data register passed in and prebaked values for data hi w/clock hi and
- // low and data lo w/clock hi and lo. This is to be used when clock and data are on the same GPIO register,
+ // low and data lo w/clock hi and lo. This is to be used when clock and data are on the same GPIO register,
// can get close to getting a bit out the door in 2 clock cycles!
- static void writeByte(uint8_t b, data_ptr_t datapin,
- data_t hival, data_t loval,
- clock_t hiclock, clock_t loclock) __attribute__((always_inline, hot)) {
+ static void writeByte(uint8_t b, data_ptr_t datapin,
+ data_t hival, data_t loval,
+ clock_t hiclock, clock_t loclock) {
writeBit<7>(b, datapin, hival, loval, hiclock, loclock);
writeBit<6>(b, datapin, hival, loval, hiclock, loclock);
writeBit<5>(b, datapin, hival, loval, hiclock, loclock);
@@ -91,9 +91,9 @@ private:
// writeByte implementation with not just registers passed in, but pre-baked values for said registers for
// data hi/lo and clock hi/lo values. Note: weird things will happen if this method is called in cases where
// the data and clock pins are on the same port! Don't do that!
- static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
- data_t hival, data_t loval,
- clock_t hiclock, clock_t loclock) __attribute__((always_inline)) {
+ static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
+ data_t hival, data_t loval,
+ clock_t hiclock, clock_t loclock) {
writeBit<7>(b, clockpin, datapin, hival, loval, hiclock, loclock);
writeBit<6>(b, clockpin, datapin, hival, loval, hiclock, loclock);
writeBit<5>(b, clockpin, datapin, hival, loval, hiclock, loclock);
@@ -105,37 +105,38 @@ private:
}
public:
- #define SPI_DELAY delaycycles< (SPI_SPEED-2) / 2>();
+ #define SPI_DELAY delaycycles<1+((SPI_SPEED-2) / 2)>();
+ #define SPI_DELAY_HALF delaycycles<1+ ((SPI_SPEED-4) / 4)>();
// write the BIT'th bit out via spi, setting the data pin then strobing the clcok
- template <uint8_t BIT> __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) {
- if(b & (1 << BIT)) {
+ 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) {
+ if(SPI_SPEED < 3) {
FastPin<CLOCK_PIN>::strobe();
- } else {
+ } else {
FastPin<CLOCK_PIN>::hi(); SPI_DELAY;
FastPin<CLOCK_PIN>::lo(); SPI_DELAY;
}
- } else {
+ } else {
FastPin<DATA_PIN>::lo();
- if(SPI_SPEED < 3) {
+ if(SPI_SPEED < 3) {
FastPin<CLOCK_PIN>::strobe();
- } else {
+ } else {
FastPin<CLOCK_PIN>::hi(); SPI_DELAY;
FastPin<CLOCK_PIN>::lo(); SPI_DELAY;
}
}
}
-
+
private:
// write the BIT'th bit out via spi, setting the data pin then strobing the clock, using the passed in pin registers to accelerate access if needed
- template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
- if(b & (1 << BIT)) {
+ template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
+ if(b & (1 << BIT)) {
FastPin<DATA_PIN>::hi(datapin);
FastPin<CLOCK_PIN>::hi(clockpin); SPI_DELAY;
FastPin<CLOCK_PIN>::lo(clockpin); SPI_DELAY;
- } else {
+ } else {
FastPin<DATA_PIN>::lo(datapin);
FastPin<CLOCK_PIN>::hi(clockpin); SPI_DELAY;
FastPin<CLOCK_PIN>::lo(clockpin); SPI_DELAY;
@@ -145,14 +146,14 @@ private:
// the version of write to use when clock and data are on separate pins with precomputed values for setting
// the clock and data pins
- template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
- data_t hival, data_t loval, clock_t hiclock, clock_t loclock) {
+ template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
+ data_t hival, data_t loval, clock_t hiclock, clock_t loclock) {
// // only need to explicitly set clock hi if clock and data are on different ports
- if(b & (1 << BIT)) {
+ if(b & (1 << BIT)) {
FastPin<DATA_PIN>::fastset(datapin, hival);
FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); SPI_DELAY;
FastPin<CLOCK_PIN>::fastset(clockpin, loclock); SPI_DELAY;
- } else {
+ } else {
// NOP;
FastPin<DATA_PIN>::fastset(datapin, loval);
FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); SPI_DELAY;
@@ -162,21 +163,21 @@ private:
// the version of write to use when clock and data are on the same port with precomputed values for the various
// combinations
- template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, data_ptr_t clockdatapin,
- data_t datahiclockhi, data_t dataloclockhi,
- data_t datahiclocklo, data_t dataloclocklo) {
+ template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, data_ptr_t clockdatapin,
+ data_t datahiclockhi, data_t dataloclockhi,
+ data_t datahiclocklo, data_t dataloclocklo) {
#if 0
writeBit<BIT>(b);
#else
- if(b & (1 << BIT)) {
- FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); SPI_DELAY;
+ if(b & (1 << BIT)) {
+ FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); SPI_DELAY_HALF;
FastPin<DATA_PIN>::fastset(clockdatapin, datahiclockhi); SPI_DELAY;
- FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); SPI_DELAY;
- } else {
+ FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); SPI_DELAY_HALF;
+ } else {
// NOP;
- FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); SPI_DELAY;
+ FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); SPI_DELAY_HALF;
FastPin<DATA_PIN>::fastset(clockdatapin, dataloclockhi); SPI_DELAY;
- FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); SPI_DELAY;
+ FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); SPI_DELAY_HALF;
}
#endif
}
@@ -190,7 +191,7 @@ public:
void release() { if(m_pSelect != NULL) { m_pSelect->release(); } } // FastPin<SELECT_PIN>::lo(); }
// Write out len bytes of the given value out over SPI. Useful for quickly flushing, say, a line of 0's down the line.
- void writeBytesValue(uint8_t value, int len) {
+ void writeBytesValue(uint8_t value, int len) {
select();
writeBytesValueRaw(value, len);
release();
@@ -201,7 +202,7 @@ public:
// TODO: Weird things may happen if software bitbanging SPI output and other pins on the output reigsters are being twiddled. Need
// to allow specifying whether or not exclusive i/o access is allowed during this process, and if i/o access is not allowed fall
// back to the degenerative code below
- while(len--) {
+ while(len--) {
writeByte(value);
}
#else
@@ -215,18 +216,18 @@ public:
register data_t datalo = FastPin<DATA_PIN>::loval();
register clock_t clockhi = FastPin<CLOCK_PIN>::hival();
register clock_t clocklo = FastPin<CLOCK_PIN>::loval();
- while(len--) {
+ while(len--) {
writeByte(value, clockpin, datapin, datahi, datalo, clockhi, clocklo);
}
} else {
- // If data and clock are on the same port then we can combine setting the data and clock pins
+ // If data and clock are on the same port then we can combine setting the data and clock pins
register data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
register data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
register data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
register data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
- while(len--) {
+ while(len--) {
writeByte(value, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
}
}
@@ -234,12 +235,12 @@ public:
}
// write a block of len uint8_ts out. Need to type this better so that explicit casts into the call aren't required.
- // note that this template version takes a class parameter for a per-byte modifier to the data.
- template <class D> void writeBytes(register uint8_t *data, int len) {
+ // note that this template version takes a class parameter for a per-byte modifier to the data.
+ template <class D> void writeBytes(register uint8_t *data, int len) {
select();
#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
uint8_t *end = data + len;
- while(data != end) {
+ while(data != end) {
writeByte(D::adjust(*data++));
}
#else
@@ -255,28 +256,28 @@ public:
register clock_t clocklo = FastPin<CLOCK_PIN>::loval();
uint8_t *end = data + len;
- while(data != end) {
+ while(data != end) {
writeByte(D::adjust(*data++), clockpin, datapin, datahi, datalo, clockhi, clocklo);
}
} else {
// FastPin<CLOCK_PIN>::hi();
- // If data and clock are on the same port then we can combine setting the data and clock pins
+ // If data and clock are on the same port then we can combine setting the data and clock pins
register data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
register data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
register data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
register data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
-
+
uint8_t *end = data + len;
- while(data != end) {
+ while(data != end) {
writeByte(D::adjust(*data++), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
}
// FastPin<CLOCK_PIN>::lo();
}
#endif
D::postBlock(len);
- release();
+ release();
}
// default version of writing a block of data out to the SPI port, with no data modifications being made
@@ -286,21 +287,22 @@ 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 of each grouping, as well as a class specifying a per
// byte of data modification to be made. (See DATA_NOP above)
- template <uint8_t SKIP, class D, EOrder RGB_ORDER> void writeBytes3(register uint8_t *data, int len, register uint8_t scale) {
+ template <uint8_t FLAGS, class D, EOrder RGB_ORDER> __attribute__((noinline)) void writePixels(PixelController<RGB_ORDER> pixels) {
select();
+ int len = pixels.mLen;
#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
// If interrupts or other things may be generating output while we're working on things, then we need
// to use this block
- uint8_t *end = data + len;
- while(data != end) {
- if(SKIP & FLAG_START_BIT) {
+ while(pixels.has(1)) {
+ if(FLAGS & FLAG_START_BIT) {
writeBit<0>(1);
}
- writeByte(D::adjust(data[SPI_B0], scale));
- writeByte(D::adjust(data[SPI_B1], scale));
- writeByte(D::adjust(data[SPI_B2], scale));
- data += SPI_ADVANCE;
+ writeByte(D::adjust(pixels.loadAndScale0()));
+ writeByte(D::adjust(pixels.loadAndScale1()));
+ writeByte(D::adjust(pixels.loadAndScale2()));
+ pixels.advanceData();
+ pixels.stepDithering();
}
#else
// If we can guaruntee that no one else will be writing data while we are running (namely, changing the values of the PORT/PDOR pins)
@@ -315,54 +317,40 @@ public:
register data_t datalo = FastPin<DATA_PIN>::loval();
register clock_t clockhi = FastPin<CLOCK_PIN>::hival();
register clock_t clocklo = FastPin<CLOCK_PIN>::loval();
- uint8_t *end = data + len;
- while(data != end) {
- if(SKIP & FLAG_START_BIT) {
+ while(pixels.has(1)) {
+ if(FLAGS & FLAG_START_BIT) {
writeBit<0>(1, clockpin, datapin, datahi, datalo, clockhi, clocklo);
}
- writeByte(D::adjust(data[SPI_B0], scale), clockpin, datapin, datahi, datalo, clockhi, clocklo);
- writeByte(D::adjust(data[SPI_B1], scale), clockpin, datapin, datahi, datalo, clockhi, clocklo);
- writeByte(D::adjust(data[SPI_B2], scale), clockpin, datapin, datahi, datalo, clockhi, clocklo);
- data += SPI_ADVANCE;
+ writeByte(D::adjust(pixels.loadAndScale0()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
+ writeByte(D::adjust(pixels.loadAndScale1()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
+ writeByte(D::adjust(pixels.loadAndScale2()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
+ pixels.advanceData();
+ pixels.stepDithering();
}
} else {
- // If data and clock are on the same port then we can combine setting the data and clock pins
+ // If data and clock are on the same port then we can combine setting the data and clock pins
register data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
register data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
register data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
register data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
-
- uint8_t *end = data + len;
- while(data != end) {
- if(SKIP & FLAG_START_BIT) {
+ while(pixels.has(1)) {
+ if(FLAGS & FLAG_START_BIT) {
writeBit<0>(1, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
}
- writeByte(D::adjust(data[SPI_B0], scale), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
- writeByte(D::adjust(data[SPI_B1], scale), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
- writeByte(D::adjust(data[SPI_B2], scale), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
- data += SPI_ADVANCE;
+ writeByte(D::adjust(pixels.loadAndScale0()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
+ writeByte(D::adjust(pixels.loadAndScale1()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
+ writeByte(D::adjust(pixels.loadAndScale2()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
+ pixels.advanceData();
+ pixels.stepDithering();
}
- }
+ }
#endif
D::postBlock(len);
release();
}
-
- 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);
- }
};
#endif
diff --git a/fastspi_nop.h b/fastspi_nop.h
new file mode 100644
index 00000000..64cd5128
--- /dev/null
+++ b/fastspi_nop.h
@@ -0,0 +1,63 @@
+#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)
+template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
+class NOPSPIOutput {
+
+public:
+ NOPSPIOutput() { /* TODO */ }
+ NOPSPIOutput(Selectable *pSelect) { /* TODO */ }
+
+ // set the object representing the selectable
+ void setSelect(Selectable *pSelect) { /* TODO */ }
+
+ // initialize the SPI subssytem
+ void init() { /* TODO */ }
+
+ // latch the CS select
+ void select() { /* TODO */ }
+
+ // release the CS select
+ void release() { /* TODO */ }
+
+ // wait until all queued up data has been written
+ void waitFully();
+
+ // 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)
+ 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 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
+ 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
+ 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);
+
+};
+
+#endif \ No newline at end of file
diff --git a/fastspi_ref.h b/fastspi_ref.h
new file mode 100644
index 00000000..709bec34
--- /dev/null
+++ b/fastspi_ref.h
@@ -0,0 +1,98 @@
+#ifndef __INC_FASTSPI_ARM_SAM_H
+#define __INC_FASTSPI_ARM_SAM_H
+
+// 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>
+class REFHardwareSPIOutput {
+ Selectable *m_pSelect;
+public:
+ SAMHardwareSPIOutput() { m_pSelect = NULL; }
+ SAMHArdwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
+
+ // set the object representing the selectable
+ void setSelect(Selectable *pSelect) { /* TODO */ }
+
+ // initialize the SPI subssytem
+ void init() { /* TODO */ }
+
+ // latch the CS 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(); } }
+
+ // wait until all queued up data has been written
+ static void waitFully() { /* TODO */ }
+
+ // write a byte out via SPI (returns immediately on writing register)
+ static void writeByte(uint8_t b) { /* TODO */ }
+
+ // write a word out via SPI (returns immediately on writing register)
+ static void writeWord(uint16_t w) { /* TODO */ }
+
+ // 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) {
+ select(); writeBytesValueRaw(value, len); release();
+ }
+
+ // 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) {
+ 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();
+ }
+
+ // A full cycle of writing a value for len bytes, including select, release, and waiting
+ void writeBytes(register 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) { /* TODO */ }
+
+ // 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) {
+ select();
+ while(data != end) {
+ 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();
+ data += (3+skip);
+ }
+ D::postBlock(len);
+ 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
diff --git a/hsv2rgb.cpp b/hsv2rgb.cpp
index b0abdab9..0844d1f2 100644
--- a/hsv2rgb.cpp
+++ b/hsv2rgb.cpp
@@ -337,7 +337,8 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb)
}
if( Y2 ) {
r = K171 + third;
- uint8_t twothirds = (third << 1);
+ //uint8_t twothirds = (third << 1);
+ uint8_t twothirds = scale8( offset8, ((256 * 2) / 3));
g = K85 + twothirds;
b = 0;
FORCE_REFERENCE(b);
@@ -350,7 +351,8 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb)
// 010
//case 2: // Y -> G
if( Y1 ) {
- uint8_t twothirds = (third << 1);
+ //uint8_t twothirds = (third << 1);
+ uint8_t twothirds = scale8( offset8, ((256 * 2) / 3));
r = K171 - twothirds;
g = K171 + third;
b = 0;
@@ -381,7 +383,8 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb)
//case 4: // A -> B
r = 0;
FORCE_REFERENCE(r);
- uint8_t twothirds = (third << 1);
+ //uint8_t twothirds = (third << 1);
+ uint8_t twothirds = scale8( offset8, ((256 * 2) / 3));
g = K171 - twothirds;
b = K85 + twothirds;
@@ -471,25 +474,3 @@ void hsv2rgb_spectrum( const struct CHSV* phsv, struct CRGB * prgb, int numLeds)
hsv2rgb_spectrum(phsv[i], prgb[i]);
}
}
-
-void fill_solid( struct CRGB * pFirstLED, int numToFill,
- const struct CRGB& color)
-{
- for( int i = 0; i < numToFill; i++) {
- pFirstLED[i] = color;
- }
-}
-
-void fill_rainbow( struct CRGB * pFirstLED, int numToFill,
- uint8_t initialhue,
- uint8_t deltahue )
-{
- CHSV hsv;
- hsv.hue = initialhue;
- hsv.val = 255;
- hsv.sat = 255;
- for( int i = 0; i < numToFill; i++) {
- hsv2rgb_rainbow( hsv, pFirstLED[i]);
- hsv.hue += deltahue;
- }
-}
diff --git a/hsv2rgb.h b/hsv2rgb.h
index d30e9aef..959c754a 100644
--- a/hsv2rgb.h
+++ b/hsv2rgb.h
@@ -45,15 +45,4 @@ 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
-
-// fill_solid - fill a range of LEDs with a solid color
-void fill_solid( struct CRGB * pFirstLED, int numToFill,
- const struct CRGB& color);
-
-// 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);
-
#endif
diff --git a/keywords.txt b/keywords.txt
index 40ba2bd2..e1aaebec 100644
--- a/keywords.txt
+++ b/keywords.txt
@@ -15,6 +15,15 @@ FastPin KEYWORD1
FastSPI KEYWORD1
FastSPI_LED2 KEYWORD1
+CRGBPalette16 KEYWORD1
+CRGBPalette256 KEYWORD1
+CHSVPalette16 KEYWORD1
+CHSVPalette256 KEYWORD1
+CHSVPalette16 KEYWORD1
+CHSVPalette256 KEYWORD1
+CRGBPalette16 KEYWORD1
+CRGBPalette256 KEYWORD1
+
#######################################
# Methods and Functions (KEYWORD2)
#######################################
@@ -26,6 +35,26 @@ getBrightness KEYWORD2
show KEYWORD2
clear KEYWORD2
showColor KEYWORD2
+setTemperature KEYWORD2
+setCorrection KEYWORD2
+setDither KEYWORD2
+countFPS KEYWORD2
+getFPS KEYWORD2
+
+# Noise methods
+inoise16_raw KEYWORD2
+inoise8_raw KEYWORD2
+inoise16 KEYWORD2
+inoise8 KEYWORD2
+fill_2dnoise16 KEYWORD2
+fill_2dnoise8 KEYWORD2
+fill_noise16 KEYWORD2
+fill_noise8 KEYWORD2
+fill_raw_2dnoise16 KEYWORD2
+fill_raw_2dnoise16into8 KEYWORD2
+fill_raw_2dnoise8 KEYWORD2
+fill_raw_noise16into8 KEYWORD2
+fill_raw_noise8 KEYWORD2
# Lib8tion methods
qadd8 KEYWORD2
@@ -35,9 +64,6 @@ add8 KEYWORD2
sub8 KEYWORD2
scale8 KEYWORD2
scale8_video KEYWORD2
-scale8_LEAVING_R1_DIRTY KEYWORD2
-nscale8_LEAVING_R1_DIRTY KEYWORD2
-scale8_video_LEAVING_R1_DIRTY KEYWORD2
cleanup_R1 KEYWORD2
nscale8x3 KEYWORD2
nscale8x3_video KEYWORD2
@@ -51,8 +77,10 @@ qmul8 KEYWORD2
abs8 KEYWORD2
dim8_raw KEYWORD2
dim8_video KEYWORD2
+dim8_lin KEYWORD2
brighten8_raw KEYWORD2
brighten8_video KEYWORD2
+brighten8_lin KEYWORD2
random8 KEYWORD2
random16 KEYWORD2
random8 KEYWORD2
@@ -63,15 +91,45 @@ random16_set_seed KEYWORD2
random16_get_seed KEYWORD2
random16_add_entropy KEYWORD2
sin16_avr KEYWORD2
-sin16_C KEYWORD2
+sin16 KEYWORD2
cos16 KEYWORD2
+sin8 KEYWORD2
+cos8 KEYWORD2
lerp8by8 KEYWORD2
lerp16by16 KEYWORD2
lerp16by8 KEYWORD2
lerp15by8 KEYWORD2
+lerp15by16 KEYWORD2
+map8 KEYWORD2
+ease8InOutQuad KEYWORD2
ease8InOutCubic KEYWORD2
ease8InOutApprox KEYWORD2
ease8InOutApprox KEYWORD2
+triwave8 KEYWORD2
+quadwave8 KEYWORD2
+cubicwave8 KEYWORD2
+sqrt16 KEYWORD2
+
+# Color util methods
+blend KEYWORD2
+nblend KEYWORD2
+ColorFromPalette KEYWORD2
+HeatColor KEYWORD2
+UpscalePalette KEYWORD2
+blend KEYWORD2
+fadeLightBy KEYWORD2
+fadeToBlackBy KEYWORD2
+fade_raw KEYWORD2
+fade_video KEYWORD2
+fill_gradient KEYWORD2
+fill_gradient_RGB KEYWORD2
+fill_palette KEYWORD2
+fill_rainbow KEYWORD2
+fill_solid KEYWORD2
+map_data_into_colors_through_palette KEYWORD2
+nblend KEYWORD2
+nscale8 KEYWORD2
+nscale8_video KEYWORD2
# HSV methods
hsv2grb_rainbow KEYWORD2
@@ -232,17 +290,27 @@ CRGB::YellowGreen KEYWORD2
# Chipsets
LPD8806 LITERAL1
WS2801 LITERAL1
+WS2803 LITERAL1
+P9813 LITERAL1
SM16716 LITERAL1
-DMX LITERAL1
+APA102 LITERAL1
+DMXSERIAL LITERAL1
+DMXSIMPLE LITERAL1
+TM1829 LITERAL1
TM1809 LITERAL1
TM1804 LITERAL1
TM1803 LITERAL1
+APA104 LITERAL1
WS2811 LITERAL1
WS2812 LITERAL1
WS2812B LITERAL1
WS2811_400 LITERAL1
NEOPIXEL LITERAL1
UCS1903 LITERAL1
+UCS1903B LITERAL1
+GW6205 LITERAL1
+GW6205B LITERAL1
+LPD1886 LITERAL1
# RGB orderings
RGB LITERAL1
@@ -252,3 +320,48 @@ GBR LITERAL1
BRG LITERAL1
BGR LITERAL1
+# hue literals
+HUE_RED LITERAL1
+HUE_ORANGE LITERAL1
+HUE_YELLOW LITERAL1
+HUE_GREEN LITERAL1
+HUE_AQUA LITERAL1
+HUE_BLUE LITERAL1
+HUE_PURPLE LITERAL1
+HUE_PINK LITERAL1
+
+# Color correction values
+TypicalSMD5050 LITERAL1
+TypicalLEDStrip LITERAL1
+Typical8mmPixel LITERAL1
+TypicalPixelString LITERAL1
+UncorrectedColor LITERAL1
+Candle LITERAL1
+Tungsten40W LITERAL1
+Tungsten100W LITERAL1
+Halogen LITERAL1
+CarbonArc LITERAL1
+HighNoonSun LITERAL1
+DirectSunlight LITERAL1
+OvercastSky LITERAL1
+ClearBlueSky LITERAL1
+WarmFluorescent LITERAL1
+StandardFluorescent LITERAL1
+CoolWhiteFluorescent LITERAL1
+FullSpectrumFluorescent LITERAL1
+GrowLightFluorescent LITERAL1
+BlackLightFluorescent LITERAL1
+MercuryVapor LITERAL1
+SodiumVapor LITERAL1
+MetalHalide LITERAL1
+HighPressureSodium LITERAL1
+UncorrectedTemperature LITERAL1
+
+# Color util literals
+FORWARD_HUES LITERAL1
+BACKWARD_HUES LITERAL1
+SHORTEST_HUES LITERAL1
+LONGEST_HUES LITERAL1
+BLEND LITERAL1
+NOBLEND LITERAL1
+
diff --git a/led_sysdefs.h b/led_sysdefs.h
new file mode 100644
index 00000000..c49410d0
--- /dev/null
+++ b/led_sysdefs.h
@@ -0,0 +1,52 @@
+#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
+#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
+
+#else
+// reuseing/abusing cli/sei defs for due
+#define cli() __disable_irq(); __disable_fault_irq();
+#define sei() __enable_irq(); __enable_fault_irq();
+
+#endif
+
+#if defined(ARDUINO) && defined(FASTLED_AVR) && ARDUINO >= 157
+#error Arduion versions 1.5.7 and later not yet supported by FastLED for AVR
+#endif
+
+// Arduino.h needed for convinience functions digitalPinToPort/BitMask/portOutputRegister and the pinMode methods.
+#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
+
+#endif
diff --git a/lib8tion.h b/lib8tion.h
index 5fb812cb..228fe8f9 100644
--- a/lib8tion.h
+++ b/lib8tion.h
@@ -2,37 +2,37 @@
#define __INC_LIB8TION_H
/*
-
+
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.
-
-
+
+
Included are:
-
-
+
+
- Saturating unsigned 8-bit add and subtract.
Instead of wrapping around if an overflow occurs,
these routines just 'clamp' the output at a maxumum
- of 255, or a minimum of 0. Useful for adding pixel
+ of 255, or a minimum of 0. Useful for adding pixel
values. E.g., qadd8( 200, 100) = 255.
-
+
qadd8( i, j) == MIN( (i + j), 0xFF )
qsub8( i, j) == MAX( (i - j), 0 )
-
+
- Saturating signed 8-bit ("7-bit") add.
qadd7( i, j) == MIN( (i + j), 0x7F)
-
-
+
+
- Scaling (down) of unsigned 8- and 16- bit values.
Scaledown value is specified in 1/256ths.
scale8( i, sc) == (i * sc) / 256
scale16by8( i, sc) == (i * sc) / 256
-
+
Example: scaling a 0-255 value down into a
range from 0-99:
downscaled = scale8( originalnumber, 100);
@@ -42,27 +42,27 @@
accidentally scale down to total black at low
dimming levels, since that would look wrong:
scale8_video( i, sc) = ((i * sc) / 256) +? 1
-
+
Example: reducing an LED brightness by a
dimming factor:
new_bright = scale8_video( orig_bright, dimming);
-
-
+
+
- Fast 8- and 16- bit unsigned random numbers.
- Significantly faster than Arduino random(), but
+ Significantly faster than Arduino random(), but
also somewhat less random. You can add entropy.
random8() == random from 0..255
random8( n) == random from 0..(N-1)
random8( n, m) == random from N..(M-1)
-
+
random16() == random from 0..65535
random16( n) == random from 0..(N-1)
random16( n, m) == random from N..(M-1)
-
+
random16_set_seed( k) == seed = k
random16_add_entropy( k) == seed += k
-
+
- Absolute value of a signed 8-bit value.
abs8( i) == abs( i)
@@ -74,31 +74,54 @@
add8( i, j) == (i + j) & 0xFF
sub8( i, j) == (i - j) & 0xFF
-
+
- Fast 16-bit approximations of sin and cos.
Input angle is a uint16_t from 0-65535.
Output is a signed int16_t from -32767 to 32767.
sin16( x) == sin( (x/32768.0) * pi) * 32767
cos16( x) == cos( (x/32768.0) * pi) * 32767
Accurate to more than 99% in all cases.
-
+
+ - Fast 8-bit approximations of sin and cos.
+ Input angle is a uint8_t from 0-255.
+ Output is an UNsigned uint8_t from 0 to 255.
+ sin8( x) == (sin( (x/128.0) * pi) * 128) + 128
+ cos8( x) == (cos( (x/128.0) * pi) * 128) + 128
+ Accurate to within about 2%.
+
+
+ - Fast 8-bit "easing in/out" function.
+ ease8InOutCubic(x) == 3(x^i) - 2(x^3)
+ ease8InOutApprox(x) ==
+ faster, rougher, approximation of cubic easing
+ ease8InOutQuad(x) == quadratic (vs cubic) easing
+
+ - Cubic, Quadratic, and Triangle wave functions.
+ Input is a uint8_t representing phase withing the wave,
+ similar to how sin8 takes an angle 'theta'.
+ Output is a uint8_t representing the amplitude of
+ the wave at that point.
+ cubicwave8( x)
+ quadwave8( x)
+ triwave8( x)
+
+ - Square root for 16-bit integers. About three times
+ 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)
- dim8_raw( x) == scale8( x, x)
- brighten8_video( x) == 255 - dim8_video( 255 - x)
- brighten8_raw( x) == 255 - dim8_raw( 255 - x)
+ dim8_video( x) == scale8_video( x, x)
+ dim8_raw( x) == scale8( x, x)
+ dim8_lin( x) == (x<128) ? ((x+1)/2) : scale8(x,x)
+ brighten8_video( x) == 255 - dim8_video( 255 - x)
+ brighten8_raw( x) == 255 - dim8_raw( 255 - x)
+ brighten8_lin( x) == 255 - dim8_lin( 255 - x)
The dimming functions in particular are suitable
for making LED light output appear more 'linear'.
- - Fast 8-bit "easing in/out" function.
- ease8InOutCubic(x) == 3(x^i) - 2(x^3)
- ease8InOutApprox(x) ==
- faster, rougher, approximation of cubic easing
-
-
- Linear interpolation between two values, with the
fraction between them expressed as an 8- or 16-bit
fixed point fraction (fract8 or fract16).
@@ -108,24 +131,42 @@
== from + (( to - from ) * fract8) / 256)
lerp16by16( fromU16, toU16, fract16 )
== from + (( to - from ) * fract16) / 65536)
-
+ map8( in, rangeStart, rangeEnd)
+ == map( in, 0, 255, rangeStart, rangeEnd);
+
- Optimized memmove, memcpy, and memset, that are
faster than standard avr-libc 1.8.
memmove8( dest, src, bytecount)
memcpy8( dest, src, bytecount)
memset8( buf, value, bytecount)
-
+
+ - Beat generators which return sine or sawtooth
+ waves in a specified number of Beats Per Minute.
+ 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)
+ = (sine(beatphase) * (high8-low8)) + low8
+ beatsin16( BPM, low16, high16)
+ = (sine(beatphase) * (high16-low16)) + low16
+ beat8( BPM) = 8-bit repeating sawtooth wave
+ beat16( BPM) = 16-bit repeating sawtooth wave
+
Lib8tion is pronounced like 'libation': lie-BAY-shun
*/
-
-
+
+
#include <stdint.h>
-#define LIB8STATIC __attribute__ ((unused)) static
+#define LIB8STATIC __attribute__ ((unused)) static inline
+#if !defined(__AVR__)
+#include <string.h>
+// 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__)
#define LIB8_ATTINY 1
@@ -134,7 +175,7 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun
#if defined(__arm__)
-#if defined(__MK20DX128__)
+#if defined(FASTLED_TEENSY3)
// Can use Cortex M4 DSP instructions
#define QADD8_C 0
#define QADD7_C 0
@@ -220,6 +261,7 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun
#define SCALE16_C 1
#define ABS8_C 1
#define MUL8_C 1
+#define QMUL8_C 1
#define ADD8_C 1
#define SUB8_C 1
#define EASE8_C 1
@@ -341,7 +383,7 @@ LIB8STATIC int8_t qadd7( int8_t i, int8_t j)
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.
@@ -373,7 +415,7 @@ LIB8STATIC uint8_t qsub8( uint8_t i, uint8_t j)
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.
@@ -383,7 +425,7 @@ LIB8STATIC uint8_t qsub8( uint8_t i, uint8_t j)
"L_%=: "
: "+a" (i)
: "a" (j) );
-
+
return i;
#else
#error "No implementation for qsub8 available."
@@ -429,8 +471,31 @@ LIB8STATIC uint8_t sub8( uint8_t i, uint8_t j)
LIB8STATIC uint8_t scale8( uint8_t i, fract8 scale)
{
#if SCALE8_C == 1
- return ((int)i * (int)(scale) ) >> 8;
+ 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"
@@ -438,13 +503,14 @@ LIB8STATIC uint8_t scale8( uint8_t i, fract8 scale)
"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
@@ -458,28 +524,43 @@ LIB8STATIC uint8_t scale8( uint8_t i, fract8 scale)
// several additional cycles.
LIB8STATIC uint8_t scale8_video( uint8_t i, fract8 scale)
{
-#if SCALE8_C == 1
- uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
- uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
+#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 nonzeroscale = (scale != 0) ? 1 : 0;
+ uint8_t j=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;
+ " 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
@@ -501,11 +582,11 @@ LIB8STATIC uint8_t scale8_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
"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
@@ -527,7 +608,7 @@ LIB8STATIC void nscale8_LEAVING_R1_DIRTY( uint8_t& i, fract8 scale)
"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 */ );
@@ -540,33 +621,67 @@ LIB8STATIC void nscale8_LEAVING_R1_DIRTY( uint8_t& i, fract8 scale)
LIB8STATIC uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
{
-#if SCALE8_C == 1
- uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
- uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
+#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 nonzeroscale = (scale != 0) ? 1 : 0;
+ uint8_t j=0;
asm volatile(
- " tst %0 \n"
- " breq L_%= \n"
- " mul %0, %1 \n"
- " mov %0, r1 \n"
- " add %0, %2 \n"
- /* R1 IS LEFT DIRTY, YOU MUST ZERO IT OUT YOURSELF */
- "L_%=: \n"
-
- : "+a" (i)
- : "a" (scale), "a" (nonzeroscale)
- : "r0", "r1");
-
- // Return the result
- return i;
+ " 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 available."
+#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()
@@ -609,9 +724,9 @@ LIB8STATIC void nscale8x3_video( uint8_t& r, uint8_t& g, uint8_t& b, fract8 scal
g = (g == 0) ? 0 : (((int)g * (int)(scale) ) >> 8) + nonzeroscale;
b = (b == 0) ? 0 : (((int)b * (int)(scale) ) >> 8) + nonzeroscale;
#elif SCALE8_AVRASM == 1
- r = scale8_video_LEAVING_R1_DIRTY( r, scale);
- g = scale8_video_LEAVING_R1_DIRTY( g, scale);
- b = scale8_video_LEAVING_R1_DIRTY( b, scale);
+ 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."
@@ -646,8 +761,8 @@ LIB8STATIC void nscale8x2_video( uint8_t& i, uint8_t& j, fract8 scale)
i = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
j = (j == 0) ? 0 : (((int)j * (int)(scale) ) >> 8) + nonzeroscale;
#elif SCALE8_AVRASM == 1
- i = scale8_video_LEAVING_R1_DIRTY( i, scale);
- j = scale8_video_LEAVING_R1_DIRTY( j, scale);
+ nscale8_video_LEAVING_R1_DIRTY( i, scale);
+ nscale8_video_LEAVING_R1_DIRTY( j, scale);
cleanup_R1();
#else
#error "No implementation for nscale8x2 available."
@@ -675,15 +790,15 @@ LIB8STATIC uint16_t scale16by8( uint16_t i, fract8 scale )
" 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"
@@ -726,7 +841,7 @@ uint16_t scale16( uint16_t i, fract16 scale )
// 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"
@@ -735,21 +850,21 @@ uint16_t scale16( uint16_t i, fract16 scale )
// 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),
@@ -781,7 +896,7 @@ LIB8STATIC uint8_t mul8( uint8_t i, uint8_t j)
: "+a" (i)
: "a" (j)
: "r0", "r1");
-
+
return i;
#else
#error "No implementation for mul8 available."
@@ -815,7 +930,7 @@ LIB8STATIC uint8_t qmul8( uint8_t i, uint8_t j)
: "+a" (i)
: "a" (j)
: "r0", "r1");
-
+
return i;
#else
#error "No implementation for qmul8 available."
@@ -830,15 +945,15 @@ LIB8STATIC int8_t abs8( int8_t i)
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
@@ -887,7 +1002,7 @@ sfract15 floatToSfract15( float f)
// appear half as bright as 'full' brightness (255), you
// have to apply a 'dimming function'.
//
-//
+//
LIB8STATIC uint8_t dim8_raw( uint8_t x)
{
@@ -899,6 +1014,17 @@ 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;
@@ -911,6 +1037,18 @@ LIB8STATIC uint8_t brighten8_video( uint8_t 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
@@ -1011,9 +1149,9 @@ LIB8STATIC int16_t sin16_avr( uint16_t theta )
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
@@ -1025,13 +1163,13 @@ LIB8STATIC int16_t sin16_avr( uint16_t theta )
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 {
@@ -1039,19 +1177,19 @@ LIB8STATIC int16_t sin16_avr( uint16_t theta )
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;
}
@@ -1061,21 +1199,21 @@ LIB8STATIC int16_t sin16_C( uint16_t theta )
{ 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;
}
@@ -1084,6 +1222,119 @@ 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);
+}
+
///////////////////////////////////////////////////////////////////////
//
@@ -1175,27 +1426,83 @@ 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
+LIB8STATIC int16_t lerp15by16( int16_t a, int16_t b, fract16 frac)
+{
+ int16_t result;
+ if( b > a) {
+ uint16_t delta = b - a;
+ uint16_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;
+}
+
+// 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;
+ uint8_t out = scale8( in, rangeWidth);
+ out += rangeStart;
+ return out;
+}
+
///////////////////////////////////////////////////////////////////////
//
// easing functions; see http://easings.net
//
-// ease8InOuCubic: 8-bit cubic ease-in / ease-out function
+// 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;
+ if( j & 0x80 ) {
+ j = 255 - j;
+ }
+ uint8_t jj = scale8( j, (j+1));
+ uint8_t jj2 = jj << 1;
+ if( i & 0x80 ) {
+ jj2 = 255 - jj2;
+ }
+ return jj2;
+}
+
+
+// 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);
uint8_t iii = scale8_LEAVING_R1_DIRTY( ii, i);
-
+
uint16_t r1 = (3 * (uint16_t)(ii)) - ( 2 * (uint16_t)(iii));
/* the code generated for the above *'s automatically
cleans up R1, so there's no need to explicitily call
cleanup_R1(); */
-
+
uint8_t result = r1;
-
+
// if we got "256", return 255:
if( r1 & 0x100 ) {
result = 255;
@@ -1228,7 +1535,7 @@ LIB8STATIC fract8 ease8InOutApprox( fract8 i)
i += (i / 2);
i += 32;
}
-
+
return i;
}
@@ -1254,9 +1561,9 @@ LIB8STATIC uint8_t ease8InOutApprox( fract8 i)
" subi %[i], 96 \n\t"
"Ldone_%=: \n\t"
-
+
: [i] "+a" (i)
- :
+ :
: "r0", "r1"
);
return i;
@@ -1267,6 +1574,219 @@ 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.
+//
+LIB8STATIC uint8_t triwave8(uint8_t in)
+{
+ if( in & 0x80) {
+ in = 255 - in;
+ }
+ uint8_t out = in << 1;
+ return out;
+}
+
+
+// quadwave8 and cubicwave8: S-shaped wave generators (like 'sine').
+// Useful for turning a one-byte 'counter' value into a
+// one-byte oscillating value that moves smoothly up and down,
+// with an 'acceleration' and 'deceleration' curve.
+//
+// These are even faster than 'sin8', and have
+// slightly different curve shapes.
+//
+
+// 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.
+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;
+ } 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;
+}
+
+
+template<class T, int F, int I> class q {
+ T i:I;
+ T f:F;
+public:
+ q(float fx) { i = fx; f = (fx-i) * (1<<F); }
+ q(uint8_t _i, uint8_t _f) {i=_i; f=_f; }
+ uint32_t operator*(uint32_t v) { return (v*i) + ((v*f)>>F); }
+ uint16_t operator*(uint16_t v) { return (v*i) + ((v*f)>>F); }
+ int32_t operator*(int32_t v) { return (v*i) + ((v*f)>>F); }
+ int16_t operator*(int16_t v) { return (v*i) + ((v*f)>>F); }
+#ifdef FASTLED_ARM
+ int operator*(int v) { return (v*i) + ((v*f)>>F); }
+#endif
+};
+
+template<class T, int F, int I> static uint32_t operator*(uint32_t v, q<T,F,I> & q) { return q * v; }
+template<class T, int F, int I> static uint16_t operator*(uint16_t v, q<T,F,I> & q) { return q * v; }
+template<class T, int F, int I> static int32_t operator*(int32_t v, q<T,F,I> & q) { return q * v; }
+template<class T, int F, int I> static int16_t operator*(int16_t v, q<T,F,I> & q) { return q * v; }
+#ifdef FASTLED_ARM
+template<class T, int F, int I> static int operator*(int v, q<T,F,I> & q) { return q * v; }
+#endif
+
+typedef q<uint8_t, 4,4> q44;
+typedef q<uint8_t, 6,2> q62;
+typedef q<uint16_t, 8,8> q88;
+typedef q<uint16_t, 12,4> q124;
+
+
+
+// Beat generators - These functions produce waves at a given
+// number of 'beats per minute'. Internally, they use
+// the Arduino function 'millis' to track elapsed time.
+// Accuracy is a bit better than one part in a thousand.
+//
+// beat8( BPM ) returns an 8-bit value that cycles 'BPM' times
+// per minute, rising from 0 to 255, resetting to zero,
+// rising up again, etc.. The output of this function
+// is suitable for feeding directly into sin8, and cos8,
+// triwave8, quadwave8, and cubicwave8.
+// beat16( BPM ) returns a 16-bit value that cycles 'BPM' times
+// 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.
+//
+// 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'.
+//
+// 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").
+// The more sophisticated way of specifying BPM allows for fractional
+// "Q8.8" fixed point number (an 'accum88') with an 8-bit integer part and
+// 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.
+//
+// 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()":
+//
+// uint8_t bright = beatsin8( 60 /*BPM*/, 192 /*dimmest*/, 255 /*brightest*/ ));
+// FastLED.setBrightness( bright );
+// FastLED.show();
+//
+// The entire animation will now pulse between brightness 192 and 255 once per second.
+
+
+// The beat generators need access to a millisecond counter.
+// On Arduino, this is "millis()". On other platforms, you'll
+// need to provide a function with this signature:
+// uint32_t get_millisecond_timer();
+// 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)
+// Forward declaration of Arduino function 'millis'.
+uint32_t millis();
+#define GET_MILLIS (millis())
+#else
+uint32_t 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)
+{
+ // 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',
+ // and then use a simple, fast bit-shift to divide by 65536.
+ //
+ // 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;
+}
+
+// beat8 generates an 8-bit 'sawtooth' wave at a given BPM
+LIB8STATIC uint8_t beat8( accum88 beats_per_minute)
+{
+ return beat16( beats_per_minute) >> 8;
+}
+
+// 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)
+{
+ uint16_t beat = beat16( beats_per_minute);
+ uint16_t beatsin = (sin16( beat) + 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)
+{
+ uint8_t beat = beat8( beats_per_minute);
+ uint8_t beatsin = sin8( beat);
+ uint8_t rangewidth = highest - lowest;
+ uint8_t scaledbeat = scale8( beatsin, rangewidth);
+ uint8_t result = lowest + scaledbeat;
+ return result;
+}
#endif
diff --git a/noise.cpp b/noise.cpp
new file mode 100644
index 00000000..f60accf7
--- /dev/null
+++ b/noise.cpp
@@ -0,0 +1,696 @@
+#include <FastLED.h>
+
+#ifdef FASTLED_AVR
+#include <avr/pgmspace.h>
+#define USE_PROGMEM
+#endif
+
+// 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
+
+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,
+ 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
+ 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
+ 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
+ 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
+ 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
+ 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
+ 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
+ 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
+ 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
+ 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
+ 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,151
+ };
+
+//
+// #define FADE_12
+#define FADE_16
+
+#ifdef FADE_12
+#define FADE logfade12
+#define LERP(a,b,u) lerp15by12(a,b,u)
+#else
+#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) {
+#if 0
+ switch(hash & 0xF) {
+ case 0: return (( x) + ( y))>>1;
+ case 1: return ((-x) + ( y))>>1;
+ case 2: return (( x) + (-y))>>1;
+ case 3: return ((-x) + (-y))>>1;
+ case 4: return (( x) + ( z))>>1;
+ case 5: return ((-x) + ( z))>>1;
+ case 6: return (( x) + (-z))>>1;
+ case 7: return ((-x) + (-z))>>1;
+ case 8: return (( y) + ( z))>>1;
+ case 9: return ((-y) + ( z))>>1;
+ case 10: return (( y) + (-z))>>1;
+ case 11: return ((-y) + (-z))>>1;
+ case 12: return (( y) + ( x))>>1;
+ case 13: return ((-y) + ( z))>>1;
+ case 14: return (( y) + (-x))>>1;
+ case 15: return ((-y) + (-z))>>1;
+ }
+#else
+ hash = hash&15;
+ int16_t u = hash<8?x:y;
+ int16_t v = hash<4?y:hash==12||hash==14?x:z;
+ if(hash&1) { u = -u; }
+ if(hash&2) { v = -v; }
+
+ return (u+v)>>1;
+#endif
+}
+
+static int16_t __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; }
+ if(hash&1) { u = -u; }
+ if(hash&2) { v = -v; }
+
+ return (u+v)>>1;
+}
+
+static int16_t __attribute__((always_inline)) grad16(uint8_t hash, int16_t x) {
+ hash = hash & 15;
+ int16_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&1) { u = -u; }
+ if(hash&2) { v = -v; }
+
+ return (u+v)>>1;
+}
+
+static int8_t __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;
+ case 1: return ((-x) + ( y))>>1;
+ case 2: return (( x) + (-y))>>1;
+ case 3: return ((-x) + (-y))>>1;
+ case 4: return (( x) + ( z))>>1;
+ case 5: return ((-x) + ( z))>>1;
+ case 6: return (( x) + (-z))>>1;
+ case 7: return ((-x) + (-z))>>1;
+ case 8: return (( y) + ( z))>>1;
+ case 9: return ((-y) + ( z))>>1;
+ case 10: return (( y) + (-z))>>1;
+ case 11: return ((-y) + (-z))>>1;
+ case 12: return (( y) + ( x))>>1;
+ case 13: return ((-y) + ( z))>>1;
+ case 14: return (( y) + (-x))>>1;
+ 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;
+ if(hash&1) { u = -u; }
+ if(hash&2) { v = -v; }
+
+ return (u+v)>>1;
+#endif
+}
+
+static int8_t __attribute__((always_inline)) grad8(uint8_t hash, int8_t x, int8_t y) {
+ hash = hash & 7;
+ int8_t u,v;
+ if(hash < 4) { u = x; v = y; } else { u = y; v = x; }
+ if(hash&1) { u = -u; }
+ if(hash&2) { v = -v; }
+
+ return (u+v)>>1;
+}
+
+static int8_t __attribute__((always_inline)) grad8(uint8_t hash, int8_t x) {
+ 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&1) { u = -u; }
+ if(hash&2) { v = -v; }
+
+ return (u+v)>>1;
+}
+
+
+#ifdef FADE_12
+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)
+{
+ //if(1) return (lerp(frac,a,b));
+ int16_t result;
+ if( b > a) {
+ uint16_t delta = b - a;
+ uint16_t scaled = scale16(delta,frac<<4);
+ result = a + scaled;
+ } else {
+ uint16_t delta = a - b;
+ uint16_t scaled = scale16(delta,frac<<4);
+ result = a - scaled;
+ }
+ return result;
+}
+#endif
+
+static int8_t __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;
+ // int8_t scaled = prod >> 8;
+ // int8_t result = a + scaled;
+ // return result;
+ int8_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;
+}
+
+int16_t inoise16_raw(uint32_t x, uint32_t y, uint32_t z)
+{
+ // Find the unit cube containing the point
+ uint8_t X = (x>>16)&0xFF;
+ uint8_t Y = (y>>16)&0xFF;
+ uint8_t Z = (z>>16)&0xFF;
+
+ // Hash cube corner coordinates
+ uint8_t A = P(X)+Y;
+ uint8_t AA = P(A)+Z;
+ uint8_t AB = P(A+1)+Z;
+ uint8_t B = P(X+1)+Y;
+ uint8_t BA = P(B) + Z;
+ uint8_t BB = P(B+1)+Z;
+
+ // Get the relative position of the point in the cube
+ uint16_t u = x & 0xFFFF;
+ uint16_t v = y & 0xFFFF;
+ uint16_t w = z & 0xFFFF;
+
+ // Get a signed version of the above for the grad function
+ int16_t xx = (u >> 1) & 0x7FFF;
+ int16_t yy = (v >> 1) & 0x7FFF;
+ int16_t zz = (w >> 1) & 0x7FFF;
+ uint16_t N = 0x8000L;
+
+ u = FADE(u); v = FADE(v); w = FADE(w);
+
+
+ // skip the log fade adjustment for the moment, otherwise here we would
+ // adjust fade values for u,v,w
+ int16_t X1 = LERP(grad16(P(AA), xx, yy, zz), grad16(P(BA), xx - N, yy, zz), u);
+ int16_t X2 = LERP(grad16(P(AB), xx, yy-N, zz), grad16(P(BB), xx - N, yy - N, zz), u);
+ int16_t X3 = LERP(grad16(P(AA+1), xx, yy, zz-N), grad16(P(BA+1), xx - N, yy, zz-N), u);
+ int16_t X4 = LERP(grad16(P(AB+1), xx, yy-N, zz-N), grad16(P(BB+1), xx - N, yy - N, zz - N), u);
+
+ int16_t Y1 = LERP(X1,X2,v);
+ int16_t Y2 = LERP(X3,X4,v);
+
+ int16_t ans = LERP(Y1,Y2,w);
+
+ return ans;
+}
+
+uint16_t inoise16(uint32_t x, uint32_t y, uint32_t z) {
+ int32_t ans = inoise16_raw(x,y,z);
+ ans = ans + 19052L;
+ uint32_t pan = ans;
+ return (pan*220L)>>7;
+ // // return scale16by8(pan,220)<<1;
+ // return ((inoise16_raw(x,y,z)+19052)*220)>>7;
+ // return scale16by8(inoise16_raw(x,y,z)+19052,220)<<1;
+}
+
+int16_t inoise16_raw(uint32_t x, uint32_t y)
+{
+ // Find the unit cube containing the point
+ uint8_t X = x>>16;
+ uint8_t Y = y>>16;
+
+ // Hash cube corner coordinates
+ uint8_t A = P(X)+Y;
+ uint8_t AA = P(A);
+ uint8_t AB = P(A+1);
+ uint8_t B = P(X+1)+Y;
+ uint8_t BA = P(B);
+ uint8_t BB = P(B+1);
+
+ // Get the relative position of the point in the cube
+ uint16_t u = x & 0xFFFF;
+ uint16_t v = y & 0xFFFF;
+
+ // Get a signed version of the above for the grad function
+ int16_t xx = (u >> 1) & 0x7FFF;
+ int16_t yy = (v >> 1) & 0x7FFF;
+ uint16_t N = 0x8000L;
+
+ u = FADE(u); v = FADE(v);
+
+ int16_t X1 = LERP(grad16(P(AA), xx, yy), grad16(P(BA), xx - N, yy), u);
+ int16_t X2 = LERP(grad16(P(AB), xx, yy-N), grad16(P(BB), xx - N, yy - N), u);
+
+ int16_t ans = LERP(X1,X2,v);
+
+ return ans;
+}
+
+uint16_t inoise16(uint32_t x, uint32_t y) {
+ int32_t ans = inoise16_raw(x,y);
+ ans = ans + 17308L;
+ uint32_t pan = ans;
+ return (pan*242L)>>7;
+ // return (uint32_t)(((int32_t)inoise16_raw(x,y)+(uint32_t)17308)*242)>>7;
+ // return scale16by8(inoise16_raw(x,y)+17308,242)<<1;
+}
+
+int16_t inoise16_raw(uint32_t x)
+{
+ // Find the unit cube containing the point
+ uint8_t X = x>>16;
+
+ // Hash cube corner coordinates
+ uint8_t A = P(X);
+ uint8_t AA = P(A);
+ uint8_t B = P(X+1);
+ uint8_t BA = P(B);
+
+ // Get the relative position of the point in the cube
+ uint16_t u = x & 0xFFFF;
+
+ // Get a signed version of the above for the grad function
+ int16_t xx = (u >> 1) & 0x7FFF;
+ uint16_t N = 0x8000L;
+
+ u = FADE(u);
+
+ int16_t ans = LERP(grad16(P(AA), xx), grad16(P(BA), xx - N), u);
+
+ return ans;
+}
+
+uint16_t inoise16(uint32_t x) {
+ return ((uint32_t)((int32_t)inoise16_raw(x) + 17308L)) << 1;
+}
+
+int8_t inoise8_raw(uint16_t x, uint16_t y, uint16_t z)
+{
+ // Find the unit cube containing the point
+ uint8_t X = x>>8;
+ uint8_t Y = y>>8;
+ uint8_t Z = z>>8;
+
+ // Hash cube corner coordinates
+ uint8_t A = P(X)+Y;
+ uint8_t AA = P(A)+Z;
+ uint8_t AB = P(A+1)+Z;
+ uint8_t B = P(X+1)+Y;
+ uint8_t BA = P(B) + Z;
+ uint8_t BB = P(B+1)+Z;
+
+ // Get the relative position of the point in the cube
+ uint8_t u = x;
+ uint8_t v = y;
+ 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;
+ uint8_t N = 0x80;
+
+ // u = FADE(u); v = FADE(v); w = FADE(w);
+ u = scale8_LEAVING_R1_DIRTY(u,u); v = scale8_LEAVING_R1_DIRTY(v,v); w = scale8(w,w);
+
+ int8_t X1 = lerp7by8(grad8(P(AA), xx, yy, zz), grad8(P(BA), xx - N, yy, zz), u);
+ int8_t X2 = lerp7by8(grad8(P(AB), xx, yy-N, zz), grad8(P(BB), xx - N, yy - N, zz), u);
+ int8_t X3 = lerp7by8(grad8(P(AA+1), xx, yy, zz-N), grad8(P(BA+1), xx - N, yy, zz-N), u);
+ int8_t X4 = lerp7by8(grad8(P(AB+1), xx, yy-N, zz-N), grad8(P(BB+1), xx - N, yy - N, zz - N), u);
+
+ int8_t Y1 = lerp7by8(X1,X2,v);
+ int8_t Y2 = lerp7by8(X3,X4,v);
+
+ int8_t ans = lerp7by8(Y1,Y2,w);
+
+ return ans;
+}
+
+uint8_t inoise8(uint16_t x, uint16_t y, uint16_t z) {
+ return scale8(76+(inoise8_raw(x,y,z)),215)<<1;
+}
+
+int8_t inoise8_raw(uint16_t x, uint16_t y)
+{
+ // Find the unit cube containing the point
+ uint8_t X = x>>8;
+ uint8_t Y = y>>8;
+
+ // Hash cube corner coordinates
+ uint8_t A = P(X)+Y;
+ uint8_t AA = P(A);
+ uint8_t AB = P(A+1);
+ uint8_t B = P(X+1)+Y;
+ uint8_t BA = P(B);
+ uint8_t BB = P(B+1);
+
+ // Get the relative position of the point in the cube
+ uint8_t u = x;
+ 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;
+ uint8_t N = 0x80;
+
+ // u = FADE(u); v = FADE(v); w = FADE(w);
+ u = scale8_LEAVING_R1_DIRTY(u,u); v = scale8(v,v);
+
+ int8_t X1 = lerp7by8(grad8(P(AA), xx, yy), grad8(P(BA), xx - N, yy), u);
+ int8_t X2 = lerp7by8(grad8(P(AB), xx, yy-N), grad8(P(BB), xx - N, yy - N), u);
+
+ int8_t ans = lerp7by8(X1,X2,v);
+
+ return ans;
+ // return scale8((70+(ans)),234)<<1;
+}
+
+uint8_t inoise8(uint16_t x, uint16_t y) {
+ return scale8(69+inoise8_raw(x,y),237)<<1;
+}
+
+int8_t inoise8_raw(uint16_t x)
+{
+ // Find the unit cube containing the point
+ uint8_t X = x>>8;
+
+ // Hash cube corner coordinates
+ uint8_t A = P(X);
+ uint8_t AA = P(A);
+ uint8_t B = P(X+1);
+ uint8_t BA = P(B);
+
+ // Get the relative position of the point in the cube
+ uint8_t u = x;
+
+ // Get a signed version of the above for the grad function
+ int8_t xx = (x>>1) & 0x7F;
+ uint8_t N = 0x80;
+
+ u = scale8(u,u);
+
+ int8_t ans = lerp7by8(grad8(P(AA), xx), grad8(P(BA), xx - N), u);
+
+ return ans;
+ // return scale8((70+(ans)),234)<<1;
+}
+
+uint8_t inoise8(uint16_t x) {
+ return scale8(69+inoise8_raw(x), 255)<<1;
+}
+
+// struct q44 {
+// uint8_t i:4;
+// uint8_t f:4;
+// q44(uint8_t _i, uint8_t _f) {i=_i; f=_f; }
+// };
+
+// uint32_t mul44(uint32_t v, q44 mulby44) {
+// return (v *mulby44.i) + ((v * mulby44.f) >> 4);
+// }
+//
+// uint16_t mul44_16(uint16_t v, q44 mulby44) {
+// return (v *mulby44.i) + ((v * mulby44.f) >> 4);
+// }
+
+void fill_raw_noise8(uint8_t *pData, uint8_t num_points, uint8_t octaves, uint16_t x, int scale, uint16_t time) {
+ uint32_t _xx = x;
+ uint32_t scx = scale;
+ for(int o = 0; o < octaves; o++) {
+ for(int i = 0,xx=_xx; i < num_points; i++, xx+=scx) {
+ pData[i] = qadd8(pData[i],inoise8(xx,time)>>o);
+ }
+
+ _xx <<= 1;
+ scx <<= 1;
+ }
+}
+
+void fill_raw_noise16into8(uint8_t *pData, uint8_t num_points, uint8_t octaves, uint32_t x, int scale, uint32_t time) {
+ uint32_t _xx = x;
+ uint32_t scx = scale;
+ for(int o = 0; o < octaves; o++) {
+ for(int i = 0,xx=_xx; i < num_points; i++, xx+=scx) {
+ uint32_t accum = (inoise16(xx,time))>>o;
+ accum += (pData[i]<<8);
+ if(accum > 65535) { accum = 65535; }
+ pData[i] = accum>>8;
+ }
+
+ _xx <<= 1;
+ scx <<= 1;
+ }
+}
+
+void fill_raw_2dnoise8(uint8_t *pData, int width, int height, uint8_t octaves, q44 freq44, fract8 amplitude, int skip, uint16_t x, int scalex, uint16_t y, int scaley, uint16_t time) {
+ if(octaves > 1) {
+ fill_raw_2dnoise8(pData, width, height, octaves-1, freq44, amplitude, skip+1, x*freq44, freq44 * scalex, y*freq44, freq44 * scaley, time);
+ } else {
+ // amplitude is always 255 on the lowest level
+ amplitude=255;
+ }
+
+ scalex *= skip;
+ scaley *= skip;
+
+ fract8 invamp = 255-amplitude;
+ uint16_t xx = x;
+ for(int i = 0; i < height; i++, y+=scaley) {
+ uint8_t *pRow = pData + (i*width);
+ xx = x;
+ for(int j = 0; j < width; j++, xx+=scalex) {
+ uint8_t noise_base = inoise8(xx,y,time);
+ noise_base = (0x80 & noise_base) ? (noise_base - 127) : (127 - noise_base);
+ noise_base = scale8(noise_base<<1,amplitude);
+ if(skip == 1) {
+ pRow[j] = scale8(pRow[j],invamp) + noise_base;
+ } else {
+ for(int ii = i; ii<(i+skip) && ii<height; ii++) {
+ uint8_t *pRow = pData + (ii*width);
+ for(int jj=j; jj<(j+skip) && jj<width; jj++) {
+ pRow[jj] = scale8(pRow[jj],invamp) + noise_base;
+ }
+ }
+ }
+ }
+ }
+}
+
+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);
+}
+
+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);
+ } else {
+ // amplitude is always 255 on the lowest level
+ amplitude=65535;
+ }
+
+ scalex *= skip;
+ scaley *= skip;
+ fract16 invamp = 65535-amplitude;
+ for(int i = 0; i < height; i+=skip, y+=scaley) {
+ uint16_t *pRow = pData + (i*width);
+ for(int j = 0,xx=x; j < width; j+=skip, xx+=scalex) {
+ uint16_t noise_base = inoise16(xx,y,time);
+ noise_base = (0x8000 & noise_base) ? noise_base - (32767) : 32767 - noise_base;
+ noise_base = scale16(noise_base<<1, amplitude);
+ if(skip==1) {
+ pRow[j] = scale16(pRow[j],invamp) + noise_base;
+ } else {
+ for(int ii = i; ii<(i+skip) && ii<height; ii++) {
+ uint16_t *pRow = pData + (ii*width);
+ for(int jj=j; jj<(j+skip) && jj<width; jj++) {
+ pRow[jj] = scale16(pRow[jj],invamp) + noise_base;
+ }
+ }
+ }
+ }
+ }
+}
+
+int32_t nmin=11111110;
+int32_t nmax=0;
+
+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) {
+ if(octaves > 1) {
+ fill_raw_2dnoise16into8(pData, width, height, octaves-1, freq44, amplitude, skip+1, x*freq44, scalex *freq44, y*freq44, scaley * freq44, time);
+ } else {
+ // amplitude is always 255 on the lowest level
+ amplitude=255;
+ }
+
+ scalex *= skip;
+ scaley *= skip;
+ uint32_t xx;
+ fract8 invamp = 255-amplitude;
+ for(int i = 0; i < height; i+=skip, y+=scaley) {
+ uint8_t *pRow = pData + (i*width);
+ xx = x;
+ for(int j = 0; j < width; j+=skip, xx+=scalex) {
+ uint16_t noise_base = inoise16(xx,y,time);
+ noise_base = (0x8000 & noise_base) ? noise_base - (32767) : 32767 - noise_base;
+ noise_base = scale8(noise_base>>7,amplitude);
+ if(skip==1) {
+ pRow[j] = qadd8(scale8(pRow[j],invamp),noise_base);
+ } else {
+ for(int ii = i; ii<(i+skip) && ii<height; ii++) {
+ uint8_t *pRow = pData + (ii*width);
+ for(int jj=j; jj<(j+skip) && jj<width; jj++) {
+ pRow[jj] = scale8(pRow[jj],invamp) + noise_base;
+ }
+ }
+ }
+ }
+ }
+}
+
+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) {
+ fill_raw_2dnoise16into8(pData, width, height, octaves, q44(2,0), 171, 1, x, scalex, y, scaley, time);
+}
+
+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,
+ uint16_t time) {
+ uint8_t V[num_leds];
+ uint8_t H[num_leds];
+
+ memset(V,0,num_leds);
+ memset(H,0,num_leds);
+
+ fill_raw_noise8(V,num_leds,octaves,x,scale,time);
+ fill_raw_noise8(H,num_leds,hue_octaves,hue_x,hue_scale,time);
+
+ for(int i = 0; i < num_leds; i++) {
+ leds[i] = CHSV(H[i],255,V[i]);
+ }
+}
+
+void fill_noise16(CRGB *leds, int num_leds,
+ uint8_t octaves, uint16_t x, int scale,
+ uint8_t hue_octaves, uint16_t hue_x, int hue_scale,
+ uint16_t time) {
+ uint8_t V[num_leds];
+ uint8_t H[num_leds];
+
+ memset(V,0,num_leds);
+ memset(H,0,num_leds);
+
+ fill_raw_noise16into8(V,num_leds,octaves,x,scale,time);
+ fill_raw_noise8(H,num_leds,hue_octaves,hue_x,hue_scale,time);
+
+ for(int i = 0; i < num_leds; i++) {
+ leds[i] = CHSV(H[i],255,V[i]);
+ }
+}
+
+void fill_2dnoise8(CRGB *leds, int width, int height, bool serpentine,
+ uint8_t octaves, uint16_t x, int xscale, uint16_t y, int yscale, uint16_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) {
+ uint8_t V[height][width];
+ uint8_t H[height][width];
+
+ memset(V,0,height*width);
+ memset(H,0,height*width);
+
+ fill_raw_2dnoise8((uint8_t*)V,width,height,octaves,x,xscale,y,yscale,time);
+ fill_raw_2dnoise8((uint8_t*)H,width,height,hue_octaves,hue_x,hue_xscale,hue_y,hue_yscale,hue_time);
+
+ int w1 = width-1;
+ int h1 = height-1;
+ for(int i = 0; i < height; i++) {
+ int wb = i*width;
+ for(int j = 0; j < width; j++) {
+ CRGB led(CHSV(H[h1-i][w1-j],255,V[i][j]));
+
+ int pos = j;
+ if(serpentine && (i & 0x1)) {
+ pos = w1-j;
+ }
+
+ if(blend) {
+ leds[wb+pos] >>= 1; leds[wb+pos] += (led>>=1);
+ } else {
+ leds[wb+pos] = led;
+ }
+ }
+ }
+}
+
+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) {
+ uint8_t V[height][width];
+ uint8_t H[height][width];
+
+ memset(V,0,height*width);
+ memset(H,0,height*width);
+
+ fill_raw_2dnoise16into8((uint8_t*)V,width,height,octaves,q44(2,0),171,1,x,xscale,y,yscale,time);
+ // fill_raw_2dnoise16into8((uint8_t*)V,width,height,octaves,x,xscale,y,yscale,time);
+ // fill_raw_2dnoise8((uint8_t*)V,width,height,hue_octaves,x,xscale,y,yscale,time);
+ fill_raw_2dnoise8((uint8_t*)H,width,height,hue_octaves,hue_x,hue_xscale,hue_y,hue_yscale,hue_time);
+
+
+ int w1 = width-1;
+ int h1 = height-1;
+ hue_shift >>= 8;
+
+ for(int i = 0; i < height; i++) {
+ int wb = i*width;
+ for(int j = 0; j < width; j++) {
+ CRGB led(CHSV(hue_shift + (H[h1-i][w1-j]),196,V[i][j]));
+
+ int pos = j;
+ if(serpentine && (i & 0x1)) {
+ pos = w1-j;
+ }
+
+ if(blend) {
+ leds[wb+pos] >>= 1; leds[wb+pos] += (led>>=1);
+ } else {
+ leds[wb+pos] = led;
+ }
+ }
+ }
+}
diff --git a/noise.h b/noise.h
new file mode 100644
index 00000000..4e11a318
--- /dev/null
+++ b/noise.h
@@ -0,0 +1,116 @@
+#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);
+
+ uint32_t getX();
+ uint32_t getY();
+ uint32_t getZ();
+ uint32_t getTime();
+
+ 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);
+
+ 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)
+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
+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)
+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);
+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.
+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,
+ uint16_t time);
+void fill_noise16(CRGB *leds, int num_leds,
+ uint8_t octaves, uint16_t x, int scale,
+ uint8_t hue_octaves, uint16_t hue_x, int hue_scale,
+ uint16_t time);
+void fill_2dnoise8(CRGB *leds, int width, int height, bool serpentine,
+ uint8_t octaves, uint16_t x, int xscale, uint16_t y, int yscale, uint16_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);
+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);
+
+#endif
diff --git a/pixeltypes.h b/pixeltypes.h
index 6d3f67f2..e112636b 100644
--- a/pixeltypes.h
+++ b/pixeltypes.h
@@ -3,6 +3,7 @@
#include <stdint.h>
#include "lib8tion.h"
+#include "color.h"
struct CRGB;
struct CHSV;
@@ -66,6 +67,16 @@ struct CHSV {
}
};
+typedef enum {
+ HUE_RED = 0,
+ HUE_ORANGE = 32,
+ HUE_YELLOW = 64,
+ HUE_GREEN = 96,
+ HUE_AQUA = 128,
+ HUE_BLUE = 160,
+ HUE_PURPLE = 192,
+ HUE_PINK = 224
+} HSVHue;
struct CRGB {
union {
@@ -100,19 +111,31 @@ struct CRGB {
inline CRGB() __attribute__((always_inline))
{
}
-
+
// allow construction from R, G, B
inline CRGB( uint8_t ir, uint8_t ig, uint8_t ib) __attribute__((always_inline))
: r(ir), g(ig), b(ib)
{
}
-
+
// allow construction from 32-bit (really 24-bit) bit 0xRRGGBB color code
inline CRGB( uint32_t colorcode) __attribute__((always_inline))
: r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF)
{
}
-
+
+ inline CRGB( LEDColorCorrection colorcode) __attribute__((always_inline))
+ : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF)
+ {
+
+ }
+
+ inline CRGB( ColorTemperature colorcode) __attribute__((always_inline))
+ : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF)
+ {
+
+ }
+
// allow copy construction
inline CRGB(const CRGB& rhs) __attribute__((always_inline))
{
@@ -120,7 +143,7 @@ struct CRGB {
g = rhs.g;
b = rhs.b;
}
-
+
// allow construction from HSV color
inline CRGB(const CHSV& rhs) __attribute__((always_inline))
{
@@ -134,7 +157,7 @@ struct CRGB {
g = rhs.g;
b = rhs.b;
return *this;
- }
+ }
// allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code
inline CRGB& operator= (const uint32_t colorcode) __attribute__((always_inline))
@@ -144,7 +167,7 @@ struct CRGB {
b = (colorcode >> 0) & 0xFF;
return *this;
}
-
+
// allow assignment from R, G, and B
inline CRGB& setRGB (uint8_t nr, uint8_t ng, uint8_t nb) __attribute__((always_inline))
{
@@ -153,28 +176,28 @@ struct CRGB {
b = nb;
return *this;
}
-
+
// allow assignment from H, S, and V
inline CRGB& setHSV (uint8_t hue, uint8_t sat, uint8_t val) __attribute__((always_inline))
{
hsv2rgb_rainbow( CHSV(hue, sat, val), *this);
return *this;
}
-
+
// allow assignment from just a Hue, saturation and value automatically at max.
inline CRGB& setHue (uint8_t hue) __attribute__((always_inline))
{
hsv2rgb_rainbow( CHSV(hue, 255, 255), *this);
return *this;
}
-
+
// allow assignment from HSV color
inline CRGB& operator= (const CHSV& rhs) __attribute__((always_inline))
{
hsv2rgb_rainbow( rhs, *this);
return *this;
}
-
+
// allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code
inline CRGB& setColorCode (uint32_t colorcode) __attribute__((always_inline))
{
@@ -183,7 +206,7 @@ struct CRGB {
b = (colorcode >> 0) & 0xFF;
return *this;
}
-
+
// add one RGB to another, saturating at 0xFF for each channel
inline CRGB& operator+= (const CRGB& rhs )
@@ -193,7 +216,7 @@ struct CRGB {
b = qadd8( b, rhs.b);
return *this;
}
-
+
// add a contstant to each channel, saturating at 0xFF
// this is NOT an operator+= overload because the compiler
// can't usefully decide when it's being passed a 32-bit
@@ -205,7 +228,7 @@ struct CRGB {
b = qadd8( b, d);
return *this;
}
-
+
// subtract one RGB from another, saturating at 0x00 for each channel
inline CRGB& operator-= (const CRGB& rhs )
{
@@ -214,7 +237,7 @@ struct CRGB {
b = qsub8( b, rhs.b);
return *this;
}
-
+
// subtract a constant from each channel, saturating at 0x00
// this is NOT an operator+= overload because the compiler
// can't usefully decide when it's being passed a 32-bit
@@ -226,14 +249,14 @@ struct CRGB {
b = qsub8( b, d);
return *this;
}
-
+
// subtract a constant of '1' from each channel, saturating at 0x00
inline CRGB& operator-- () __attribute__((always_inline))
{
subtractFromRGB(1);
return *this;
}
-
+
// subtract a constant of '1' from each channel, saturating at 0x00
inline CRGB operator-- (int DUMMY_ARG) __attribute__((always_inline))
{
@@ -248,7 +271,7 @@ struct CRGB {
addToRGB(1);
return *this;
}
-
+
// add a constant of '1' from each channel, saturating at 0xFF
inline CRGB operator++ (int DUMMY_ARG) __attribute__((always_inline))
{
@@ -265,7 +288,16 @@ struct CRGB {
b /= d;
return *this;
}
-
+
+ // right shift each of the channels by a constant
+ inline CRGB& operator>>= (uint8_t d)
+ {
+ r >>= d;
+ g >>= d;
+ b >>= d;
+ return *this;
+ }
+
// multiply each of the channels by a constant,
// saturating each channel at 0xFF
inline CRGB& operator*= (uint8_t d )
@@ -286,7 +318,7 @@ struct CRGB {
nscale8x3_video( r, g, b, scaledown);
return *this;
}
-
+
// %= is a synonym for nscale8_video. Think of it is scaling down
// by "a percentage"
inline CRGB& operator%= (uint8_t scaledown )
@@ -301,7 +333,7 @@ struct CRGB {
nscale8x3_video( r, g, b, 255 - fadefactor);
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.
@@ -317,7 +349,7 @@ struct CRGB {
nscale8x3( r, g, b, 255 - fadefactor);
return *this;
}
-
+
// "or" operator brings each channel up to the higher of the two values
inline CRGB& operator|= (const CRGB& rhs )
{
@@ -333,7 +365,7 @@ struct CRGB {
if( d > b) b = d;
return *this;
}
-
+
// "and" operator brings each channel down to the lower of the two values
inline CRGB& operator&= (const CRGB& rhs )
{
@@ -349,13 +381,13 @@ struct CRGB {
if( d < b) b = d;
return *this;
}
-
+
// this allows testing a CRGB for zero-ness
inline operator bool() const __attribute__((always_inline))
{
return r || g || b;
}
-
+
// invert each channel
inline CRGB operator- ()
{
@@ -365,19 +397,28 @@ struct CRGB {
retval.b = 255 - b;
return retval;
}
-
-
+
+#ifdef SmartMatrix_h
+ operator rgb24() const {
+ rgb24 ret;
+ ret.red = r;
+ ret.green = g;
+ ret.blue = b;
+ return ret;
+ }
+#endif
+
inline uint8_t getLuma ( ) {
//Y' = 0.2126 R' + 0.7152 G' + 0.0722 B'
// 54 183 18 (!)
-
+
uint8_t luma = scale8_LEAVING_R1_DIRTY( r, 54) + \
scale8_LEAVING_R1_DIRTY( g, 183) + \
scale8_LEAVING_R1_DIRTY( b, 18);
cleanup_R1();
return luma;
}
-
+
inline uint8_t getAverageLight( ) {
const uint8_t eightysix = 86;
uint8_t avg = scale8_LEAVING_R1_DIRTY( r, eightysix) + \
@@ -396,7 +437,29 @@ struct CRGB {
green = (green * factor) / 256;
blue = (blue * factor) / 256;
}
-
+
+ inline CRGB lerp8( CRGB & other, fract8 frac)
+ {
+ CRGB ret;
+
+ ret.r = lerp8by8(r,other.r,frac);
+ ret.g = lerp8by8(g,other.g,frac);
+ ret.b = lerp8by8(b,other.b,frac);
+
+ return ret;
+ }
+
+ inline CRGB lerp16( CRGB & other, fract16 frac)
+ {
+ CRGB ret;
+
+ ret.r = lerp16by16(r<<8,other.r<<8,frac)>>8;
+ ret.g = lerp16by16(g<<8,other.g<<8,frac)>>8;
+ ret.b = lerp16by16(b<<8,other.b<<8,frac)>>8;
+
+ return ret;
+ }
+
typedef enum {
AliceBlue=0xF0F8FF,
Amethyst=0x9966CC,
@@ -424,6 +487,7 @@ struct CRGB {
DarkCyan=0x008B8B,
DarkGoldenrod=0xB8860B,
DarkGray=0xA9A9A9,
+ DarkGrey=0xA9A9A9,
DarkGreen=0x006400,
DarkKhaki=0xBDB76B,
DarkMagenta=0x8B008B,
@@ -435,11 +499,13 @@ struct CRGB {
DarkSeaGreen=0x8FBC8F,
DarkSlateBlue=0x483D8B,
DarkSlateGray=0x2F4F4F,
+ DarkSlateGrey=0x2F4F4F,
DarkTurquoise=0x00CED1,
DarkViolet=0x9400D3,
DeepPink=0xFF1493,
DeepSkyBlue=0x00BFFF,
DimGray=0x696969,
+ DimGrey=0x696969,
DodgerBlue=0x1E90FF,
FireBrick=0xB22222,
FloralWhite=0xFFFAF0,
@@ -450,6 +516,7 @@ struct CRGB {
Gold=0xFFD700,
Goldenrod=0xDAA520,
Gray=0x808080,
+ Grey=0x808080,
Green=0x008000,
GreenYellow=0xADFF2F,
Honeydew=0xF0FFF0,
@@ -473,6 +540,7 @@ struct CRGB {
LightSeaGreen=0x20B2AA,
LightSkyBlue=0x87CEFA,
LightSlateGray=0x778899,
+ LightSlateGrey=0x778899,
LightSteelBlue=0xB0C4DE,
LightYellow=0xFFFFE0,
Lime=0x00FF00,
@@ -526,6 +594,7 @@ struct CRGB {
SkyBlue=0x87CEEB,
SlateBlue=0x6A5ACD,
SlateGray=0x708090,
+ SlateGrey=0x708090,
Snow=0xFFFAFA,
SpringGreen=0x00FF7F,
SteelBlue=0x4682B4,
@@ -541,7 +610,7 @@ struct CRGB {
Yellow=0xFFFF00,
YellowGreen=0x9ACD32
} HTMLColorCode;
- static uint32_t Squant;
+ // static uint32_t Squant;
};
@@ -618,7 +687,7 @@ inline CRGB operator/( const CRGB& p1, uint8_t d)
return CRGB( p1.r/d, p1.g/d, p1.b/d);
}
-
+
__attribute__((always_inline))
inline CRGB operator&( const CRGB& p1, const CRGB& p2)
{
@@ -626,7 +695,7 @@ inline CRGB operator&( const CRGB& p1, const CRGB& p2)
p1.g < p2.g ? p1.g : p2.g,
p1.b < p2.b ? p1.b : p2.b);
}
-
+
__attribute__((always_inline))
inline CRGB operator|( const CRGB& p1, const CRGB& p2)
{
diff --git a/power_mgt.cpp b/power_mgt.cpp
new file mode 100644
index 00000000..336430fd
--- /dev/null
+++ b/power_mgt.cpp
@@ -0,0 +1,176 @@
+#include "FastLED.h"
+#include "power_mgt.h"
+
+//// POWER MANAGEMENT
+
+// These power usage values are approximate, and your exact readings
+// will be slightly (10%?) different from these.
+//
+// They were arrived at by actually measuing the power draw of a number
+// of different LED strips, and a bunch of closed-loop-feedback testing
+// to make sure that if we USE these values, we stay at or under
+// the target power consumption.
+// Actual power consumption is much, much more complicated and has
+// to include things like voltage drop, etc., etc.
+// However, this is good enough for most cases, and almost certainly better
+// than no power management at all.
+//
+// You're welcome to adjust these values as needed; there may eventually be an API
+// for changing these on the fly, but it saves codespace and RAM to have them
+// be compile-time constants.
+
+static const uint8_t gRed_mW = 16 * 5; // 16mA @ 5v = 80mW
+static const uint8_t gGreen_mW = 11 * 5; // 11mA @ 5v = 55mW
+static const uint8_t gBlue_mW = 15 * 5; // 15mA @ 5v = 75mW
+static const uint8_t gDark_mW = 1 * 5; // 1mA @ 5v = 5mW
+
+// Alternate calibration by RAtkins via pre-PSU wattage measurments;
+// these are all probably about 20%-25% too high due to PSU heat losses,
+// but if you're measuring wattage on the PSU input side, this may
+// be a better set of calibrations. (WS2812B)
+// static const uint8_t gRed_mW = 100;
+// static const uint8_t gGreen_mW = 48;
+// static const uint8_t gBlue_mW = 100;
+// static const uint8_t gDark_mW = 12;
+
+
+#define POWER_LED 1
+#define POWER_DEBUG_PRINT 0
+
+
+// Power consumed by the MCU
+static const uint8_t gMCU_mW = 25 * 5; // 25mA @ 5v = 125 mW
+
+
+static uint32_t gMaxPowerInMilliwatts = (uint32_t)(400) * (uint32_t)(5); // 400mA @ 5v default to avoid USB bricking
+static uint8_t gMaxPowerIndicatorLEDPinNumber = 13; // default = Arduino onboard LED pin. set to zero to skip this.
+
+
+uint32_t calculate_unscaled_power_mW( const CRGB* ledbuffer, uint16_t numLeds ) //25354
+{
+ 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++;
+ green32 += *p++;
+ 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;
+}
+
+
+
+// sets brightness to
+// - no more than target_brightness
+// - no more than max_mW milliwatts
+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());
+ pCur = pCur->next();
+ }
+
+#if POWER_DEBUG_PRINT == 1
+ 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 ) {
+ Serial.print("power demand at scaled brightness mW = ");
+ Serial.println( requested_power_mW);
+ }
+ Serial.print("power limit mW = ");
+ Serial.println( max_power_mW);
+#endif
+
+ if( requested_power_mW < max_power_mW) {
+#if POWER_LED > 0
+ if( gMaxPowerIndicatorLEDPinNumber ) {
+ digitalWrite(gMaxPowerIndicatorLEDPinNumber, LOW); // turn the LED off
+ }
+#endif
+#if POWER_DEBUG_PRINT == 1
+ Serial.print("demand is under the limit");
+#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 # = ");
+ Serial.println( recommended_brightness);
+
+ 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;
+}
+
+
+void set_max_power_indicator_LED( uint8_t pinNumber)
+{
+ gMaxPowerIndicatorLEDPinNumber = pinNumber;
+}
+
+void set_max_power_in_volts_and_milliamps( uint8_t volts, uint32_t milliamps)
+{
+ gMaxPowerInMilliwatts = (uint32_t)((uint32_t)(volts) * milliamps);
+}
+
+void set_max_power_in_milliwatts( uint32_t powerInmW)
+{
+ gMaxPowerInMilliwatts = powerInmW;
+}
+
+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 );
+}
+
+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 );
+}
+
diff --git a/power_mgt.h b/power_mgt.h
new file mode 100644
index 00000000..60b92e95
--- /dev/null
+++ b/power_mgt.h
@@ -0,0 +1,48 @@
+#ifndef POWER_MGT_H
+#define POWER_MGT_H
+
+#include "pixeltypes.h"
+
+// Power Control setup functions
+//
+// Example:
+// set_max_power_in_volts_and_milliamps( 5, 400);
+//
+void set_max_power_in_volts_and_milliamps( uint8_t volts, uint32_t milliamps);
+void set_max_power_in_milliwatts( uint32_t powerInmW);
+
+void set_max_power_indicator_LED( uint8_t pinNumber); // zero = no indicator LED
+
+
+// Power Control 'show' and 'delay' functions
+//
+// These are drop-in replacements for FastLED.show() and FastLED.delay()
+// In order to use these, you have to actually replace your calls to
+// FastLED.show() and FastLED.delay() with these two functions.
+//
+// Example:
+// // was: FastLED.show();
+// // now is:
+// show_at_max_brightness_for_power();
+//
+void show_at_max_brightness_for_power();
+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.
+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);
+
+
+// POWER_MGT_H
+#endif
diff --git a/preview_changes.txt b/preview_changes.txt
new file mode 100644
index 00000000..88a9f9bc
--- /dev/null
+++ b/preview_changes.txt
@@ -0,0 +1,22 @@
+* 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
diff --git a/release_notes.txt b/release_notes.md
index 11d2a6c6..3df2d4e2 100644
--- a/release_notes.txt
+++ b/release_notes.md
@@ -1,31 +1,72 @@
-* FastLED2
+FastLED2.1
+==========
-* Full release of the library
+* Added support for the following platforms:
+ * Arduino due
+ * Teensy 3.1
+* Added the following LED chipsets:
+ * USC1903_400
+ * GW6205 / GW6205_400
+ * APA102
+ * APA104
+ * LPD1886
+ * P9813
+ * SmartMatrix
+* Added multiple examples:
+ * ColorPalette - show off the color palette code
+ * ColorTemperature - show off the color correction code
+ * Fire2012
+ * Fire2012WithPalette
+ * Multiple led controller examples
+ * Noise
+ * NoisePlayground
+ * NoisePlusPalette
+ * SmartMatrix - show off SmartMatrix support
+ * XYMatrix - show how to use a mtrix layout of leds
+* Added color correction
+* Added dithering
+* Added power management support
+* Added support for color palettes
+* Added easing functions
+* Added fast trig functions
+* Added simplex noise functions
+* Added color utility functions
+* Fixed DMXSERIAL/DMXSIMPLE support
+* Timing adjustments for existing SPI chipsets
+* Cleaned up the code layout to make platform support easier
+* Many bug fixes
+* A number of performance/memory improvements
+* Remove Squant (takes up space!)
-* Release Candidate 6
+FastLED2
+========
+
+## Full release of the library
+
+## Release Candidate 6
* Rename library, offically, to FastLED, move to github
* Update keywords with all the new stuffs
-Release Candidate 5
+## Release Candidate 5
* Gemma and Trinket: supported except for global "setBrightness"
-Release Candidate 4
+## Release Candidate 4
* Added NEOPIXEL as a synonym for WS2811
* Fix WS2811/WS2812B timings, bring it in line to exactly 1.25ns/bit.
* Fix handling of constant color definitions (damn you, gcc!)
-Release Candidate 3
+## Release Candidate 3
* Fixed bug when Clock and Data were on the same port
* Added ability to set pixel color directly from HSV
* Added ability to retrieve current random16 seed
-Release Candidate 2
+## Release Candidate 2
* mostly bug fixes
* Fix SPI macro definitions for latest teensy3 software update
* Teensy 2 compilation fix
* hsv2rgb_rainbow performance fix
-Release Candidate 1
+## Release Candidate 1
* New unified/simplified API for adding/using controllers
* fleshout clockless chip support
* add hsv (spectrum and rainbow style colors)
@@ -40,11 +81,11 @@ Release Candidate 1
* high speed math methods
* rich CRGB structure
-Preview 3
+## Preview 3
* True hardware SPI support for teensy (up to 20Mbit output!)
* Minor bug fixes/tweaks
-Preview 2
+## Preview 2
* Rename pin class to FastPin
* Replace latch with select, more accurate description of what it does
* Enforce intra-frame timing for ws2801s
@@ -59,7 +100,7 @@ en if you're using the hardware SPI pins
# Add pin definitions for Teensy++ 2.0
-Preview 1
+## Preview 1
* Initial release
diff --git a/smartmatrix_t3.h b/smartmatrix_t3.h
new file mode 100644
index 00000000..ba77d149
--- /dev/null
+++ b/smartmatrix_t3.h
@@ -0,0 +1,79 @@
+#ifndef __INC_SMARTMATRIX_T3_H
+#define __INC_SMARTMATRIX_T3_H
+
+#ifdef SmartMatrix_h
+#include<SmartMatrix.h>
+
+extern SmartMatrix *pSmartMatrix;
+
+// note - dmx simple must be included before FastSPI for this code to be enabled
+class CSmartMatrixController : public CLEDController {
+ SmartMatrix matrix;
+
+public:
+ // initialize the LED controller
+ virtual void init() {
+ // Initialize 32x32 LED Matrix
+ matrix.begin();
+ matrix.setBrightness(255);
+ matrix.setColorCorrection(ccNone);
+
+ // Clear screen
+ clearLeds(0);
+ matrix.swapBuffers();
+ pSmartMatrix = &matrix;
+ }
+
+ // clear out/zero out the given number of leds.
+ virtual void clearLeds(int nLeds) {
+ const rgb24 black = {0,0,0};
+ matrix.fillScreen(black);
+ matrix.swapBuffers();
+ }
+
+ // set all the leds on the controller to a given color
+ virtual void showColor(const struct CRGB & data, int nLeds,CRGB scale) {
+ PixelController<RGB> pixels(data, nLeds, scale, getDither());
+ rgb24 *md = matrix.backBuffer();
+ while(nLeds--) {
+ md->red = pixels.loadAndScale0();
+ md->green = pixels.loadAndScale1();
+ md->blue = pixels.loadAndScale2();
+ md++;
+ pixels.stepDithering();
+ }
+ matrix.swapBuffers();
+ }
+
+ // 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
+ virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
+ PixelController<RGB> pixels(data, nLeds, scale, getDither());
+#ifdef SMART_MATRIX_CAN_TRIPLE_BUFFER
+ rgb24 *md = matrix.getRealBackBuffer();
+#else
+ rgb24 *md = matrix.backBuffer();
+#endif
+ while(nLeds--) {
+ md->red = pixels.loadAndScale0();
+ md->green = pixels.loadAndScale1();
+ md->blue = pixels.loadAndScale2();
+ md++;
+ pixels.advanceData();
+ pixels.stepDithering();
+ }
+ matrix.swapBuffers();
+#ifdef SMART_MATRIX_CAN_TRIPLE_BUFFER
+ matrix.setBackBuffer((rgb24*)data);
+#endif
+ }
+
+#ifdef SUPPORT_ARGB
+ // as above, but every 4th uint8_t is assumed to be alpha channel data, and will be skipped
+ virtual void show(const struct CARGB *data, int nLeds, CRGB scale) = 0;
+#endif
+};
+
+#endif
+
+#endif