This commit is contained in:
Alex
2021-02-03 23:34:17 +03:00
parent 9be9c966d7
commit 3439e5cd3d
193 changed files with 35322 additions and 0 deletions

View File

@@ -0,0 +1,275 @@
#define FASTLED_INTERNAL
#include "FastLED.h"
#if defined(__SAM3X8E__)
volatile uint32_t fuckit;
#endif
FASTLED_NAMESPACE_BEGIN
void *pSmartMatrix = NULL;
CFastLED FastLED;
CLEDController *CLEDController::m_pHead = NULL;
CLEDController *CLEDController::m_pTail = NULL;
static uint32_t lastshow = 0;
uint32_t _frame_cnt=0;
uint32_t _retry_cnt=0;
// uint32_t CRGB::Squant = ((uint32_t)((__TIME__[4]-'0') * 28))<<16 | ((__TIME__[6]-'0')*50)<<8 | ((__TIME__[7]-'0')*28);
CFastLED::CFastLED() {
// clear out the array of led controllers
// m_nControllers = 0;
m_Scale = 255;
m_nFPS = 0;
m_pPowerFunc = NULL;
m_nPowerData = 0xFFFFFFFF;
}
CLEDController &CFastLED::addLeds(CLEDController *pLed,
struct CRGB *data,
int nLedsOrOffset, int nLedsIfOffset) {
int nOffset = (nLedsIfOffset > 0) ? nLedsOrOffset : 0;
int nLeds = (nLedsIfOffset > 0) ? nLedsIfOffset : nLedsOrOffset;
pLed->init();
pLed->setLeds(data + nOffset, nLeds);
FastLED.setMaxRefreshRate(pLed->getMaxRefreshRate(),true);
return *pLed;
}
void CFastLED::show(uint8_t scale) {
// guard against showing too rapidly
while(m_nMinMicros && ((micros()-lastshow) < m_nMinMicros));
lastshow = micros();
// If we have a function for computing power, use it!
if(m_pPowerFunc) {
scale = (*m_pPowerFunc)(scale, m_nPowerData);
}
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();
}
int CFastLED::count() {
int x = 0;
CLEDController *pCur = CLEDController::head();
while( pCur) {
++x;
pCur = pCur->next();
}
return x;
}
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) {
while(m_nMinMicros && ((micros()-lastshow) < m_nMinMicros));
lastshow = micros();
// If we have a function for computing power, use it!
if(m_pPowerFunc) {
scale = (*m_pPowerFunc)(scale, m_nPowerData);
}
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(bool writeData) {
if(writeData) {
showColor(CRGB(0,0,0), 0);
}
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();
do {
#ifndef FASTLED_ACCURATE_CLOCK
// make sure to allow at least one ms to pass to ensure the clock moves
// forward
::delay(1);
#endif
show();
yield();
}
while((millis()-start) < ms);
}
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();
}
}
//
// template<int m, int n> void transpose8(unsigned char A[8], unsigned char B[8]) {
// uint32_t x, y, t;
//
// // Load the array and pack it into x and y.
// y = *(unsigned int*)(A);
// x = *(unsigned int*)(A+4);
//
// // x = (A[0]<<24) | (A[m]<<16) | (A[2*m]<<8) | A[3*m];
// // y = (A[4*m]<<24) | (A[5*m]<<16) | (A[6*m]<<8) | A[7*m];
//
// // pre-transform x
// t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
// t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
//
// // pre-transform y
// t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
// t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
//
// // final transform
// t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
// y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
// x = t;
//
// B[7*n] = y; y >>= 8;
// B[6*n] = y; y >>= 8;
// B[5*n] = y; y >>= 8;
// B[4*n] = y;
//
// B[3*n] = x; x >>= 8;
// B[2*n] = x; x >>= 8;
// B[n] = x; x >>= 8;
// B[0] = x;
// // B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x>>0;
// // B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y>>0;
// }
//
// void transposeLines(Lines & out, Lines & in) {
// transpose8<1,2>(in.bytes, out.bytes);
// transpose8<1,2>(in.bytes + 8, out.bytes + 1);
// }
extern int noise_min;
extern int noise_max;
void CFastLED::countFPS(int nFrames) {
static int br = 0;
static uint32_t lastframe = 0; // millis();
if(br++ >= nFrames) {
uint32_t now = millis();
now -= lastframe;
if(now == 0) {
now = 1; // prevent division by zero below
}
m_nFPS = (br * 1000) / now;
br = 0;
lastframe = millis();
}
}
void CFastLED::setMaxRefreshRate(uint16_t refresh, bool constrain) {
if(constrain) {
// if we're constraining, the new value of m_nMinMicros _must_ be higher than previously (because we're only
// allowed to slow things down if constraining)
if(refresh > 0) {
m_nMinMicros = ((1000000 / refresh) > m_nMinMicros) ? (1000000 / refresh) : m_nMinMicros;
}
} else if(refresh > 0) {
m_nMinMicros = 1000000 / refresh;
} else {
m_nMinMicros = 0;
}
}
extern "C" int atexit(void (* /*func*/ )()) { return 0; }
#ifdef FASTLED_NEEDS_YIELD
extern "C" void yield(void) { }
#endif
#ifdef NEED_CXX_BITS
namespace __cxxabiv1
{
#if !defined(ESP8266) && !defined(ESP32)
extern "C" void __cxa_pure_virtual (void) {}
#endif
/* guard variables */
/* The ABI requires a 64-bit type. */
__extension__ typedef int __guard __attribute__((mode(__DI__)));
extern "C" int __cxa_guard_acquire (__guard *) __attribute__((weak));
extern "C" void __cxa_guard_release (__guard *) __attribute__((weak));
extern "C" void __cxa_guard_abort (__guard *) __attribute__((weak));
extern "C" int __cxa_guard_acquire (__guard *g)
{
return !*(char *)(g);
}
extern "C" void __cxa_guard_release (__guard *g)
{
*(char *)g = 1;
}
extern "C" void __cxa_guard_abort (__guard *)
{
}
}
#endif
FASTLED_NAMESPACE_END

View File

@@ -0,0 +1,592 @@
#ifndef __INC_FASTSPI_LED2_H
#define __INC_FASTSPI_LED2_H
///@file FastLED.h
/// central include file for FastLED, defines the CFastLED class/object
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
#define FASTLED_HAS_PRAGMA_MESSAGE
#endif
#define FASTLED_VERSION 3004000
#ifndef FASTLED_INTERNAL
# ifdef FASTLED_HAS_PRAGMA_MESSAGE
# pragma message "FastLED version 3.004.000"
# else
# warning FastLED version 3.004.000 (Not really a warning, just telling you here.)
# endif
#endif
#ifndef __PROG_TYPES_COMPAT__
#define __PROG_TYPES_COMPAT__
#endif
#ifdef SmartMatrix_h
#include <SmartMatrix.h>
#endif
#ifdef DmxSimple_h
#include <DmxSimple.h>
#endif
#ifdef DmxSerial_h
#include <DMXSerial.h>
#endif
#include <stdint.h>
#include "cpp_compat.h"
#include "fastled_config.h"
#include "led_sysdefs.h"
// Utility functions
#include "fastled_delay.h"
#include "bitswap.h"
#include "controller.h"
#include "fastpin.h"
#include "fastspi_types.h"
#include "dmx.h"
#include "platforms.h"
#include "fastled_progmem.h"
#include "lib8tion.h"
#include "pixeltypes.h"
#include "hsv2rgb.h"
#include "colorutils.h"
#include "pixelset.h"
#include "colorpalettes.h"
#include "noise.h"
#include "power_mgt.h"
#include "fastspi.h"
#include "chipsets.h"
FASTLED_NAMESPACE_BEGIN
/// definitions for the spi chipset constants
enum ESPIChipsets {
LPD6803,
LPD8806,
WS2801,
WS2803,
SM16716,
P9813,
APA102,
SK9822,
DOTSTAR
};
enum ESM { SMART_MATRIX };
enum OWS2811 { OCTOWS2811,OCTOWS2811_400, OCTOWS2813};
enum SWS2812 { WS2812SERIAL };
#ifdef HAS_PIXIE
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class PIXIE : public PixieController<DATA_PIN, RGB_ORDER> {};
#endif
#ifdef FASTLED_HAS_CLOCKLESS
template<uint8_t DATA_PIN> class NEOPIXEL : public WS2812Controller800Khz<DATA_PIN, GRB> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class SM16703 : public SM16703Controller<DATA_PIN, RGB_ORDER> {};
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 TM1812 : public TM1809Controller800Khz<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 UCS1904 : public UCS1904Controller800Khz<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class UCS2903 : public UCS2903Controller<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2812 : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2852 : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2812B : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class GS1903 : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class SK6812 : public SK6812Controller<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class SK6822 : public SK6822Controller<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class APA106 : public SK6822Controller<DATA_PIN, RGB_ORDER> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class PL9823 : public PL9823Controller<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 WS2813 : public WS2813Controller<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 GE8822 : public GE8822Controller800Khz<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> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class LPD1886_8BIT : public LPD1886Controller1250Khz_8bit<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
#endif
enum EBlockChipsets {
#ifdef PORTA_FIRST_PIN
WS2811_PORTA,
WS2813_PORTA,
WS2811_400_PORTA,
TM1803_PORTA,
UCS1903_PORTA,
#endif
#ifdef PORTB_FIRST_PIN
WS2811_PORTB,
WS2813_PORTB,
WS2811_400_PORTB,
TM1803_PORTB,
UCS1903_PORTB,
#endif
#ifdef PORTC_FIRST_PIN
WS2811_PORTC,
WS2813_PORTC,
WS2811_400_PORTC,
TM1803_PORTC,
UCS1903_PORTC,
#endif
#ifdef PORTD_FIRST_PIN
WS2811_PORTD,
WS2813_PORTD,
WS2811_400_PORTD,
TM1803_PORTD,
UCS1903_PORTD,
#endif
#ifdef HAS_PORTDC
WS2811_PORTDC,
WS2813_PORTDC,
WS2811_400_PORTDC,
TM1803_PORTDC,
UCS1903_PORTDC,
#endif
};
#if defined(LIB8_ATTINY)
#define NUM_CONTROLLERS 2
#else
#define NUM_CONTROLLERS 8
#endif
typedef uint8_t (*power_func)(uint8_t scale, uint32_t data);
/// High level controller interface for FastLED. This class manages controllers, global settings and trackings
/// such as brightness, and refresh rates, and provides access functions for driving led data to controllers
/// via the show/showColor/clear methods.
/// @nosubgrouping
class CFastLED {
// int m_nControllers;
uint8_t m_Scale; ///< The current global brightness scale setting
uint16_t m_nFPS; ///< Tracking for current FPS value
uint32_t m_nMinMicros; ///< minimum µs between frames, used for capping frame rates.
uint32_t m_nPowerData; ///< max power use parameter
power_func m_pPowerFunc; ///< function for overriding brightness when using FastLED.show();
public:
CFastLED();
/// Add a CLEDController instance to the world. Exposed to the public to allow people to implement their own
/// CLEDController objects or instances. There are two ways to call this method (as well as the other addLeds)
/// variations. The first is with 3 arguments, in which case the arguments are the controller, a pointer to
/// led data, and the number of leds used by this controller. The second is with 4 arguments, in which case
/// the first two arguments are the same, the third argument is an offset into the CRGB data where this controller's
/// CRGB data begins, and the fourth argument is the number of leds for this controller object.
/// @param pLed - the led controller being added
/// @param data - base point to an array of CRGB data structures
/// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array
/// @param nLedsIfOffset - number of leds (4 argument version)
/// @returns a reference to the added controller
static CLEDController &addLeds(CLEDController *pLed, struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0);
/// @name Adding SPI based controllers
//@{
/// Add an SPI based CLEDController instance to the world.
/// There are two ways to call this method (as well as the other addLeds)
/// variations. The first is with 2 arguments, in which case the arguments are a pointer to
/// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case
/// the first argument is the same, the second argument is an offset into the CRGB data where this controller's
/// CRGB data begins, and the third argument is the number of leds for this controller object.
///
/// This method also takes a 1 to 5 template parameters for identifying the specific chipset, data and clock pins,
/// RGB ordering, and SPI data rate
/// @param data - base point to an array of CRGB data structures
/// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array
/// @param nLedsIfOffset - number of leds (4 argument version)
/// @tparam CHIPSET - the chipset type
/// @tparam DATA_PIN - the optional data pin for the leds (if omitted, will default to the first hardware SPI MOSI pin)
/// @tparam CLOCK_PIN - the optional clock pin for the leds (if omitted, will default to the first hardware SPI clock pin)
/// @tparam RGB_ORDER - the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in)
/// @tparam SPI_DATA_RATE - the data rate to drive the SPI clock at, defined using DATA_RATE_MHZ or DATA_RATE_KHZ macros
/// @returns a reference to the added controller
template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER, uint32_t SPI_DATA_RATE > CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
switch(CHIPSET) {
case LPD6803: { static LPD6803Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case LPD8806: { static LPD8806Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case WS2801: { static WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case WS2803: { static WS2803Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SM16716: { static SM16716Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case DOTSTAR:
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
}
}
template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN > static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
switch(CHIPSET) {
case LPD6803: { static LPD6803Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
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 DOTSTAR:
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822: { static SK9822Controller<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 > static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
switch(CHIPSET) {
case LPD6803: { static LPD6803Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
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 DOTSTAR:
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
}
}
#ifdef SPI_DATA
template<ESPIChipsets CHIPSET> static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
return addLeds<CHIPSET, SPI_DATA, SPI_CLOCK, RGB>(data, nLedsOrOffset, nLedsIfOffset);
}
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, uint32_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
//@}
#ifdef FASTLED_HAS_CLOCKLESS
/// @name Adding 3-wire led controllers
//@{
/// Add a clockless (aka 3wire, also DMX) based CLEDController instance to the world.
/// There are two ways to call this method (as well as the other addLeds)
/// variations. The first is with 2 arguments, in which case the arguments are a pointer to
/// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case
/// the first argument is the same, the second argument is an offset into the CRGB data where this controller's
/// CRGB data begins, and the third argument is the number of leds for this controller object.
///
/// This method also takes a 2 to 3 template parameters for identifying the specific chipset, data pin, and rgb ordering
/// RGB ordering, and SPI data rate
/// @param data - base point to an array of CRGB data structures
/// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array
/// @param nLedsIfOffset - number of leds (4 argument version)
/// @tparam CHIPSET - the chipset type (required)
/// @tparam DATA_PIN - the optional data pin for the leds (required)
/// @tparam RGB_ORDER - the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in)
/// @returns a reference to the added controller
template<template<uint8_t DATA_PIN, EOrder RGB_ORDER> class CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
static CHIPSET<DATA_PIN, RGB_ORDER> c;
return addLeds(&c, 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<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);
}
#if defined(__FASTLED_HAS_FIBCC) && (__FASTLED_HAS_FIBCC == 1)
template<uint8_t NUM_LANES, template<uint8_t DATA_PIN, EOrder RGB_ORDER> class CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER=RGB>
static CLEDController &addLeds(struct CRGB *data, int nLeds) {
static __FIBCC<CHIPSET, DATA_PIN, NUM_LANES, RGB_ORDER> c;
return addLeds(&c, data, nLeds);
}
#endif
#ifdef FASTSPI_USE_DMX_SIMPLE
template<EClocklessChipsets CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER=RGB>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
{
switch(CHIPSET) {
case DMX: { static DMXController<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
}
}
#endif
//@}
#endif
/// @name Adding 3rd party library controllers
//@{
/// Add a 3rd party library based CLEDController instance to the world.
/// There are two ways to call this method (as well as the other addLeds)
/// variations. The first is with 2 arguments, in which case the arguments are a pointer to
/// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case
/// the first argument is the same, the second argument is an offset into the CRGB data where this controller's
/// CRGB data begins, and the third argument is the number of leds for this controller object. This class includes the SmartMatrix
/// and OctoWS2811 based controllers
///
/// This method also takes a 1 to 2 template parameters for identifying the specific chipset and rgb ordering
/// RGB ordering, and SPI data rate
/// @param data - base point to an array of CRGB data structures
/// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array
/// @param nLedsIfOffset - number of leds (4 argument version)
/// @tparam CHIPSET - the chipset type (required)
/// @tparam RGB_ORDER - the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in)
/// @returns a reference to the added controller
template<template<EOrder RGB_ORDER> class CHIPSET, EOrder RGB_ORDER>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
static CHIPSET<RGB_ORDER> c;
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
}
template<template<EOrder RGB_ORDER> class CHIPSET>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
static CHIPSET<RGB> c;
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
}
#ifdef USE_OCTOWS2811
template<OWS2811 CHIPSET, EOrder RGB_ORDER>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
{
switch(CHIPSET) {
case OCTOWS2811: { static COctoWS2811Controller<RGB_ORDER,WS2811_800kHz> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
case OCTOWS2811_400: { static COctoWS2811Controller<RGB_ORDER,WS2811_400kHz> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
#ifdef WS2813_800kHz
case OCTOWS2813: { static COctoWS2811Controller<RGB_ORDER,WS2813_800kHz> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
#endif
}
}
template<OWS2811 CHIPSET>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
{
return addLeds<CHIPSET,GRB>(data,nLedsOrOffset,nLedsIfOffset);
}
#endif
#ifdef USE_WS2812SERIAL
template<SWS2812 CHIPSET, int DATA_PIN, EOrder RGB_ORDER>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
{
static CWS2812SerialController<DATA_PIN,RGB_ORDER> controller;
return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset);
}
#endif
#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
//@}
#ifdef FASTLED_HAS_BLOCKLESS
/// @name adding parallel output controllers
//@{
/// Add a block based CLEDController instance to the world.
/// There are two ways to call this method (as well as the other addLeds)
/// variations. The first is with 2 arguments, in which case the arguments are a pointer to
/// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case
/// the first argument is the same, the second argument is an offset into the CRGB data where this controller's
/// CRGB data begins, and the third argument is the number of leds for this controller object.
///
/// This method also takes a 2 to 3 template parameters for identifying the specific chipset and rgb ordering
/// RGB ordering, and SPI data rate
/// @param data - base point to an array of CRGB data structures
/// @param nLedsOrOffset - number of leds (3 argument version) or offset into the data array
/// @param nLedsIfOffset - number of leds (4 argument version)
/// @tparam CHIPSET - the chipset/port type (required)
/// @tparam NUM_LANES - how many parallel lanes of output to write
/// @tparam RGB_ORDER - the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in)
/// @returns a reference to the added controller
template<EBlockChipsets CHIPSET, int NUM_LANES, EOrder RGB_ORDER>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
switch(CHIPSET) {
#ifdef PORTA_FIRST_PIN
case WS2811_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2811_400_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2813_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
case TM1803_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case UCS1903_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
#endif
#ifdef PORTB_FIRST_PIN
case WS2811_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2811_400_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2813_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
case TM1803_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case UCS1903_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
#endif
#ifdef PORTC_FIRST_PIN
case WS2811_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2811_400_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2813_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
case TM1803_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case UCS1903_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
#endif
#ifdef PORTD_FIRST_PIN
case WS2811_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2811_400_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2813_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
case TM1803_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case UCS1903_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
#endif
#ifdef HAS_PORTDC
case WS2811_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES,NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2811_400_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES,NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2813_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
case TM1803_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case UCS1903_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
#endif
}
}
template<EBlockChipsets CHIPSET, int NUM_LANES>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
return addLeds<CHIPSET,NUM_LANES,GRB>(data,nLedsOrOffset,nLedsIfOffset);
}
//@}
#endif
/// Set the global brightness scaling
/// @param scale a 0-255 value for how much to scale all leds before writing them out
void setBrightness(uint8_t scale) { m_Scale = scale; }
/// Get the current global brightness setting
/// @returns the current global brightness value
uint8_t getBrightness() { return m_Scale; }
/// Set the maximum power to be used, given in volts and milliamps.
/// @param volts - how many volts the leds are being driven at (usually 5)
/// @param milliamps - the maximum milliamps of power draw you want
inline void setMaxPowerInVoltsAndMilliamps(uint8_t volts, uint32_t milliamps) { setMaxPowerInMilliWatts(volts * milliamps); }
/// Set the maximum power to be used, given in milliwatts
/// @param milliwatts - the max power draw desired, in milliwatts
inline void setMaxPowerInMilliWatts(uint32_t milliwatts) { m_pPowerFunc = &calculate_max_brightness_for_power_mW; m_nPowerData = milliwatts; }
/// Update all our controllers with the current led colors, using the passed in brightness
/// @param scale temporarily override the scale
void show(uint8_t scale);
/// Update all our controllers with the current led colors
void show() { show(m_Scale); }
/// clear the leds, wiping the local array of data, optionally black out the leds as well
/// @param writeData whether or not to write out to the leds as well
void clear(bool writeData = false);
/// clear out the local data array
void clearData();
/// Set all leds on all controllers to the given color/scale
/// @param color what color to set the leds to
/// @param scale what brightness scale to show at
void showColor(const struct CRGB & color, uint8_t scale);
/// Set all leds on all controllers to the given color
/// @param color what color to set the leds to
void showColor(const struct CRGB & color) { showColor(color, m_Scale); }
/// Delay for the given number of milliseconds. Provided to allow the library to be used on platforms
/// that don't have a delay function (to allow code to be more portable). Note: this will call show
/// constantly to drive the dithering engine (and will call show at least once).
/// @param ms the number of milliseconds to pause for
void delay(unsigned long ms);
/// Set a global color temperature. Sets the color temperature for all added led strips, overriding whatever
/// previous color temperature those controllers may have had
/// @param temp A CRGB structure describing the color temperature
void setTemperature(const struct CRGB & temp);
/// Set a global color correction. Sets the color correction for all added led strips,
/// overriding whatever previous color correction those controllers may have had.
/// @param correction A CRGB structure describin the color correction.
void setCorrection(const struct CRGB & correction);
/// Set the dithering mode. Sets the dithering mode for all added led strips, overriding
/// whatever previous dithering option those controllers may have had.
/// @param ditherMode - what type of dithering to use, either BINARY_DITHER or DISABLE_DITHER
void setDither(uint8_t ditherMode = BINARY_DITHER);
/// Set the maximum refresh rate. This is global for all leds. Attempts to
/// call show faster than this rate will simply wait. Note that the refresh rate
/// defaults to the slowest refresh rate of all the leds added through addLeds. If
/// you wish to set/override this rate, be sure to call setMaxRefreshRate _after_
/// adding all of your leds.
/// @param refresh - maximum refresh rate in hz
/// @param constrain - constrain refresh rate to the slowest speed yet set
void setMaxRefreshRate(uint16_t refresh, bool constrain=false);
/// for debugging, will keep track of time between calls to countFPS, and every
/// nFrames calls, it will update an internal counter for the current FPS.
/// @todo make this a rolling counter
/// @param nFrames - how many frames to time for determining FPS
void countFPS(int nFrames=25);
/// Get the number of frames/second being written out
/// @returns the most recently computed FPS value
uint16_t getFPS() { return m_nFPS; }
/// Get how many controllers have been registered
/// @returns the number of controllers (strips) that have been added with addLeds
int count();
/// Get a reference to a registered controller
/// @returns a reference to the Nth controller
CLEDController & operator[](int x);
/// Get the number of leds in the first controller
/// @returns the number of LEDs in the first controller
int size() { return (*this)[0].size(); }
/// Get a pointer to led data for the first controller
/// @returns pointer to the CRGB buffer for the first controller
CRGB *leds() { return (*this)[0].leds(); }
};
#define FastSPI_LED FastLED
#define FastSPI_LED2 FastLED
#ifndef LEDS
#define LEDS FastLED
#endif
extern CFastLED FastLED;
// Warnings for undefined things
#ifndef HAS_HARDWARE_PIN_SUPPORT
#warning "No pin/port mappings found, pin access will be slightly slower. See fastpin.h for info."
#define NO_HARDWARE_PIN_SUPPORT
#endif
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,28 @@
#define FASTLED_INTERNAL
#include "FastLED.h"
/// Simplified form of bits rotating function. Based on code found here - http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt - rotating
/// data into LSB for a faster write (the code using this data can happily walk the array backwards)
void transpose8x1_noinline(unsigned char *A, unsigned char *B) {
uint32_t x, y, t;
// Load the array and pack it into x and y.
y = *(unsigned int*)(A);
x = *(unsigned int*)(A+4);
// pre-transform x
t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
// pre-transform y
t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
// final transform
t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
x = t;
*((uint32_t*)B) = y;
*((uint32_t*)(B+4)) = x;
}

View File

@@ -0,0 +1,276 @@
#ifndef __INC_BITSWAP_H
#define __INC_BITSWAP_H
#include "FastLED.h"
FASTLED_NAMESPACE_BEGIN
///@file bitswap.h
///Functions for rotating bits/bytes
///@defgroup Bitswap Bit swapping/rotate
///Functions for doing a rotation of bits/bytes used by parallel output
///@{
#if defined(FASTLED_ARM) || defined(FASTLED_ESP8266)
/// structure representing 8 bits of access
typedef union {
uint8_t raw;
struct {
uint32_t a0:1;
uint32_t a1:1;
uint32_t a2:1;
uint32_t a3:1;
uint32_t a4:1;
uint32_t a5:1;
uint32_t a6:1;
uint32_t a7:1;
};
} just8bits;
/// structure representing 32 bits of access
typedef struct {
uint32_t a0:1;
uint32_t a1:1;
uint32_t a2:1;
uint32_t a3:1;
uint32_t a4:1;
uint32_t a5:1;
uint32_t a6:1;
uint32_t a7:1;
uint32_t b0:1;
uint32_t b1:1;
uint32_t b2:1;
uint32_t b3:1;
uint32_t b4:1;
uint32_t b5:1;
uint32_t b6:1;
uint32_t b7:1;
uint32_t c0:1;
uint32_t c1:1;
uint32_t c2:1;
uint32_t c3:1;
uint32_t c4:1;
uint32_t c5:1;
uint32_t c6:1;
uint32_t c7:1;
uint32_t d0:1;
uint32_t d1:1;
uint32_t d2:1;
uint32_t d3:1;
uint32_t d4:1;
uint32_t d5:1;
uint32_t d6:1;
uint32_t d7:1;
} sub4;
/// union containing a full 8 bytes to swap the bit orientation on
typedef union {
uint32_t word[2];
uint8_t bytes[8];
struct {
sub4 a;
sub4 b;
};
} bitswap_type;
#define SWAPSA(X,N) out. X ## 0 = in.a.a ## N; \
out. X ## 1 = in.a.b ## N; \
out. X ## 2 = in.a.c ## N; \
out. X ## 3 = in.a.d ## N;
#define SWAPSB(X,N) out. X ## 0 = in.b.a ## N; \
out. X ## 1 = in.b.b ## N; \
out. X ## 2 = in.b.c ## N; \
out. X ## 3 = in.b.d ## N;
#define SWAPS(X,N) out. X ## 0 = in.a.a ## N; \
out. X ## 1 = in.a.b ## N; \
out. X ## 2 = in.a.c ## N; \
out. X ## 3 = in.a.d ## N; \
out. X ## 4 = in.b.a ## N; \
out. X ## 5 = in.b.b ## N; \
out. X ## 6 = in.b.c ## N; \
out. X ## 7 = in.b.d ## N;
/// Do an 8byte by 8bit rotation
__attribute__((always_inline)) inline void swapbits8(bitswap_type in, bitswap_type & out) {
// SWAPS(a.a,7);
// SWAPS(a.b,6);
// SWAPS(a.c,5);
// SWAPS(a.d,4);
// SWAPS(b.a,3);
// SWAPS(b.b,2);
// SWAPS(b.c,1);
// SWAPS(b.d,0);
// SWAPSA(a.a,7);
// SWAPSA(a.b,6);
// SWAPSA(a.c,5);
// SWAPSA(a.d,4);
//
// SWAPSB(a.a,7);
// SWAPSB(a.b,6);
// SWAPSB(a.c,5);
// SWAPSB(a.d,4);
//
// SWAPSA(b.a,3);
// SWAPSA(b.b,2);
// SWAPSA(b.c,1);
// SWAPSA(b.d,0);
// //
// SWAPSB(b.a,3);
// SWAPSB(b.b,2);
// SWAPSB(b.c,1);
// SWAPSB(b.d,0);
for(int i = 0; i < 8; ++i) {
just8bits work;
work.a3 = in.word[0] >> 31;
work.a2 = in.word[0] >> 23;
work.a1 = in.word[0] >> 15;
work.a0 = in.word[0] >> 7;
in.word[0] <<= 1;
work.a7 = in.word[1] >> 31;
work.a6 = in.word[1] >> 23;
work.a5 = in.word[1] >> 15;
work.a4 = in.word[1] >> 7;
in.word[1] <<= 1;
out.bytes[i] = work.raw;
}
}
/// Slow version of the 8 byte by 8 bit rotation
__attribute__((always_inline)) inline void slowswap(unsigned char *A, unsigned char *B) {
for(int row = 0; row < 7; ++row) {
uint8_t x = A[row];
uint8_t bit = (1<<row);
unsigned char *p = B;
for(uint32_t mask = 1<<7 ; mask ; mask >>= 1) {
if(x & mask) {
*p++ |= bit;
} else {
*p++ &= ~bit;
}
}
// B[7] |= (x & 0x01) << row; x >>= 1;
// B[6] |= (x & 0x01) << row; x >>= 1;
// B[5] |= (x & 0x01) << row; x >>= 1;
// B[4] |= (x & 0x01) << row; x >>= 1;
// B[3] |= (x & 0x01) << row; x >>= 1;
// B[2] |= (x & 0x01) << row; x >>= 1;
// B[1] |= (x & 0x01) << row; x >>= 1;
// B[0] |= (x & 0x01) << row; x >>= 1;
}
}
void transpose8x1_noinline(unsigned char *A, unsigned char *B);
/// Simplified form of bits rotating function. Based on code found here - http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt - rotating
/// data into LSB for a faster write (the code using this data can happily walk the array backwards)
__attribute__((always_inline)) inline void transpose8x1(unsigned char *A, unsigned char *B) {
uint32_t x, y, t;
// Load the array and pack it into x and y.
y = *(unsigned int*)(A);
x = *(unsigned int*)(A+4);
// pre-transform x
t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
// pre-transform y
t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
// final transform
t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
x = t;
*((uint32_t*)B) = y;
*((uint32_t*)(B+4)) = x;
}
/// Simplified form of bits rotating function. Based on code found here - http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt
__attribute__((always_inline)) inline void transpose8x1_MSB(unsigned char *A, unsigned char *B) {
uint32_t x, y, t;
// Load the array and pack it into x and y.
y = *(unsigned int*)(A);
x = *(unsigned int*)(A+4);
// pre-transform x
t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
// pre-transform y
t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
// final transform
t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
x = t;
B[7] = y; y >>= 8;
B[6] = y; y >>= 8;
B[5] = y; y >>= 8;
B[4] = y;
B[3] = x; x >>= 8;
B[2] = x; x >>= 8;
B[1] = x; x >>= 8;
B[0] = x; /* */
}
/// templated bit-rotating function. Based on code found here - http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt
template<int m, int n>
__attribute__((always_inline)) inline void transpose8(unsigned char *A, unsigned char *B) {
uint32_t x, y, t;
// Load the array and pack it into x and y.
if(m == 1) {
y = *(unsigned int*)(A);
x = *(unsigned int*)(A+4);
} else {
x = (A[0]<<24) | (A[m]<<16) | (A[2*m]<<8) | A[3*m];
y = (A[4*m]<<24) | (A[5*m]<<16) | (A[6*m]<<8) | A[7*m];
}
// pre-transform x
t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
// pre-transform y
t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
// final transform
t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
x = t;
B[7*n] = y; y >>= 8;
B[6*n] = y; y >>= 8;
B[5*n] = y; y >>= 8;
B[4*n] = y;
B[3*n] = x; x >>= 8;
B[2*n] = x; x >>= 8;
B[n] = x; x >>= 8;
B[0] = x;
// B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x>>0;
// B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y>>0;
}
#endif
FASTLED_NAMESPACE_END
///@}
#endif

View File

@@ -0,0 +1,619 @@
#ifndef __INC_CHIPSETS_H
#define __INC_CHIPSETS_H
#include "FastLED.h"
#include "pixeltypes.h"
///@file chipsets.h
/// contains the bulk of the definitions for the various LED chipsets supported.
FASTLED_NAMESPACE_BEGIN
///@defgroup chipsets
/// Implementations of CLEDController classes for various led chipsets.
///
///@{
#if defined(ARDUINO) //&& defined(SoftwareSerial_h)
#if defined(SoftwareSerial_h) || defined(__SoftwareSerial_h)
#include <SoftwareSerial.h>
#define HAS_PIXIE
/// Adafruit Pixie controller class
/// @tparam DATAPIN the pin to write data out on
/// @tparam RGB_ORDER the RGB ordering for the led data
template<uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class PixieController : public CPixelLEDController<RGB_ORDER> {
SoftwareSerial Serial;
CMinWait<2000> mWait;
public:
PixieController() : Serial(-1, DATA_PIN) {}
protected:
virtual void init() {
Serial.begin(115200);
mWait.mark();
}
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mWait.wait();
while(pixels.has(1)) {
uint8_t r = pixels.loadAndScale0();
Serial.write(r);
uint8_t g = pixels.loadAndScale1();
Serial.write(g);
uint8_t b = pixels.loadAndScale2();
Serial.write(b);
pixels.advanceData();
pixels.stepDithering();
}
mWait.mark();
}
};
// template<SoftwareSerial & STREAM, EOrder RGB_ORDER = RGB>
// class PixieController : public PixieBaseController<STREAM, RGB_ORDER> {
// public:
// virtual void init() {
// STREAM.begin(115200);
// }
// };
#endif
#endif
///@name Clocked chipsets - nominally SPI based these chipsets have a data and a clock line.
///@{
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// LPD8806 controller class - takes data/clock/select pin values (N.B. should take an SPI definition?)
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// LPD8806 controller class.
/// @tparam DATA_PIN the data pin for these leds
/// @tparam CLOCK_PIN the clock pin for these leds
/// @tparam RGB_ORDER the RGB ordering for these leds
/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(12)
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12) >
class LPD8806Controller : public CPixelLEDController<RGB_ORDER> {
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) + ((data && (data<254)) & 0x01); }
__attribute__((always_inline)) inline static void postBlock(int len) {
SPI::writeBytesValueRaw(0, ((len*3+63)>>6));
}
};
SPI mSPI;
public:
LPD8806Controller() {}
virtual void init() {
mSPI.init();
}
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mSPI.template writePixels<0, LPD8806_ADJUST, RGB_ORDER>(pixels);
}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// WS2801 definition - takes data/clock/select pin values (N.B. should take an SPI definition?)
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// WS2801 controller class.
/// @tparam DATA_PIN the data pin for these leds
/// @tparam CLOCK_PIN the clock pin for these leds
/// @tparam RGB_ORDER the RGB ordering for these leds
/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(1)
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(1)>
class WS2801Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
SPI mSPI;
CMinWait<1000> mWaitDelay;
public:
WS2801Controller() {}
virtual void init() {
mSPI.init();
mWaitDelay.mark();
}
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mWaitDelay.wait();
mSPI.template writePixels<0, DATA_NOP, RGB_ORDER>(pixels);
mWaitDelay.mark();
}
};
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(25)>
class WS2803Controller : public WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED> {};
/// LPD6803 controller class (LPD1101).
/// 16 bit (1 bit - const "1", 5 bit - red, 5 bit - green, 5 bit blue).
/// In chip CMODE pin must be set to 1 (inside oscillator mode).
/// Datasheet: https://cdn-shop.adafruit.com/datasheets/LPD6803.pdf
/// @tparam DATA_PIN the data pin for these leds
/// @tparam CLOCK_PIN the clock pin for these leds
/// @tparam RGB_ORDER the RGB ordering for these leds
/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(12)
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12)>
class LPD6803Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
SPI mSPI;
void startBoundary() { mSPI.writeByte(0); mSPI.writeByte(0); mSPI.writeByte(0); mSPI.writeByte(0); }
public:
LPD6803Controller() {}
virtual void init() {
mSPI.init();
}
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mSPI.select();
startBoundary();
while(pixels.has(1)) {
register uint16_t command;
command = 0x8000;
command |= (pixels.loadAndScale0() & 0xF8) << 7; // red is the high 5 bits
command |= (pixels.loadAndScale1() & 0xF8) << 2; // green is the middle 5 bits
mSPI.writeByte((command >> 8) & 0xFF);
command |= pixels.loadAndScale2() >> 3 ; // blue is the low 5 bits
mSPI.writeByte(command & 0xFF);
pixels.stepDithering();
pixels.advanceData();
}
//endBoundary(pixels.size());
mSPI.waitFully();
mSPI.release();
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// APA102 definition - takes data/clock/select pin values (N.B. should take an SPI definition?)
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// APA102 controller class.
/// @tparam DATA_PIN the data pin for these leds
/// @tparam CLOCK_PIN the clock pin for these leds
/// @tparam RGB_ORDER the RGB ordering for these leds
/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(12)
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12)>
class APA102Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
SPI mSPI;
void startBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); }
void endBoundary(int nLeds) { int nDWords = (nLeds/32); do { mSPI.writeByte(0xFF); mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); } while(nDWords--); }
inline void writeLed(uint8_t brightness, uint8_t b0, uint8_t b1, uint8_t b2) __attribute__((always_inline)) {
#ifdef FASTLED_SPI_BYTE_ONLY
mSPI.writeByte(0xE0 | brightness);
mSPI.writeByte(b0);
mSPI.writeByte(b1);
mSPI.writeByte(b2);
#else
uint16_t b = 0xE000 | (brightness << 8) | (uint16_t)b0;
mSPI.writeWord(b);
uint16_t w = b1 << 8;
w |= b2;
mSPI.writeWord(w);
#endif
}
public:
APA102Controller() {}
virtual void init() {
mSPI.init();
}
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mSPI.select();
uint8_t s0 = pixels.getScale0(), s1 = pixels.getScale1(), s2 = pixels.getScale2();
#if FASTLED_USE_GLOBAL_BRIGHTNESS == 1
const uint16_t maxBrightness = 0x1F;
uint16_t brightness = ((((uint16_t)max(max(s0, s1), s2) + 1) * maxBrightness - 1) >> 8) + 1;
s0 = (maxBrightness * s0 + (brightness >> 1)) / brightness;
s1 = (maxBrightness * s1 + (brightness >> 1)) / brightness;
s2 = (maxBrightness * s2 + (brightness >> 1)) / brightness;
#else
const uint8_t brightness = 0x1F;
#endif
startBoundary();
while (pixels.has(1)) {
writeLed(brightness, pixels.loadAndScale0(0, s0), pixels.loadAndScale1(0, s1), pixels.loadAndScale2(0, s2));
pixels.stepDithering();
pixels.advanceData();
}
endBoundary(pixels.size());
mSPI.waitFully();
mSPI.release();
}
};
/// SK9822 controller class.
/// @tparam DATA_PIN the data pin for these leds
/// @tparam CLOCK_PIN the clock pin for these leds
/// @tparam RGB_ORDER the RGB ordering for these leds
/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(24)
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(24)>
class SK9822Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
SPI mSPI;
void startBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); }
void endBoundary(int nLeds) { int nLongWords = (nLeds/32); do { mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); } while(nLongWords--); }
inline void writeLed(uint8_t brightness, uint8_t b0, uint8_t b1, uint8_t b2) __attribute__((always_inline)) {
#ifdef FASTLED_SPI_BYTE_ONLY
mSPI.writeByte(0xE0 | brightness);
mSPI.writeByte(b0);
mSPI.writeByte(b1);
mSPI.writeByte(b2);
#else
uint16_t b = 0xE000 | (brightness << 8) | (uint16_t)b0;
mSPI.writeWord(b);
uint16_t w = b1 << 8;
w |= b2;
mSPI.writeWord(w);
#endif
}
public:
SK9822Controller() {}
virtual void init() {
mSPI.init();
}
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mSPI.select();
uint8_t s0 = pixels.getScale0(), s1 = pixels.getScale1(), s2 = pixels.getScale2();
#if FASTLED_USE_GLOBAL_BRIGHTNESS == 1
const uint16_t maxBrightness = 0x1F;
uint16_t brightness = ((((uint16_t)max(max(s0, s1), s2) + 1) * maxBrightness - 1) >> 8) + 1;
s0 = (maxBrightness * s0 + (brightness >> 1)) / brightness;
s1 = (maxBrightness * s1 + (brightness >> 1)) / brightness;
s2 = (maxBrightness * s2 + (brightness >> 1)) / brightness;
#else
const uint8_t brightness = 0x1F;
#endif
startBoundary();
while (pixels.has(1)) {
writeLed(brightness, pixels.loadAndScale0(0, s0), pixels.loadAndScale1(0, s1), pixels.loadAndScale2(0, s2));
pixels.stepDithering();
pixels.advanceData();
}
endBoundary(pixels.size());
mSPI.waitFully();
mSPI.release();
}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// P9813 definition - takes data/clock/select pin values (N.B. should take an SPI definition?)
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// P9813 controller class.
/// @tparam DATA_PIN the data pin for these leds
/// @tparam CLOCK_PIN the clock pin for these leds
/// @tparam RGB_ORDER the RGB ordering for these leds
/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(10)
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(10)>
class P9813Controller : public CPixelLEDController<RGB_ORDER> {
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();
}
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mSPI.select();
writeBoundary();
while(pixels.has(1)) {
writeLed(pixels.loadAndScale0(), pixels.loadAndScale1(), pixels.loadAndScale2());
pixels.advanceData();
pixels.stepDithering();
}
writeBoundary();
mSPI.waitFully();
mSPI.release();
}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SM16716 definition - takes data/clock/select pin values (N.B. should take an SPI definition?)
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SM16716 controller class.
/// @tparam DATA_PIN the data pin for these leds
/// @tparam CLOCK_PIN the clock pin for these leds
/// @tparam RGB_ORDER the RGB ordering for these leds
/// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(16)
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(16)>
class SM16716Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
SPI mSPI;
void writeHeader() {
// Write out 50 zeros to the spi line (6 blocks of 8 followed by two single bit writes)
mSPI.select();
mSPI.template writeBit<0>(0);
mSPI.writeByte(0);
mSPI.writeByte(0);
mSPI.writeByte(0);
mSPI.template writeBit<0>(0);
mSPI.writeByte(0);
mSPI.writeByte(0);
mSPI.writeByte(0);
mSPI.waitFully();
mSPI.release();
}
public:
SM16716Controller() {}
virtual void init() {
mSPI.init();
}
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
// 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 writePixels<FLAG_START_BIT, DATA_NOP, RGB_ORDER>( pixels );
writeHeader();
}
};
/// @}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Clockless template instantiations - see clockless.h for how the timing values are used
//
// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit.
// At T=0 : the line is raised hi to start a bit
// At T=T1 : the line is dropped low to transmit a zero bit
// At T=T1+T2 : the line is dropped low to transmit a one bit
// At T=T1+T2+T3 : the cycle is concluded (next bit can be sent)
//
// The units used for T1, T2, and T3 is nanoseconds.
// For 8MHz/16MHz/24MHz frequencies, these values are also guaranteed
// to be integral multiples of an 8MHz clock (125ns increments).
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef FASTLED_HAS_CLOCKLESS
/// @name clockless controllers
/// Provides timing definitions for the variety of clockless controllers supplied by the library.
/// @{
// Allow clock that clockless controller is based on to have different
// frequency than the CPU.
#if !defined(CLOCKLESS_FREQUENCY)
#define CLOCKLESS_FREQUENCY F_CPU
#endif
// 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 defined(__LGT8F__) || (CLOCKLESS_FREQUENCY == 8000000 || CLOCKLESS_FREQUENCY == 16000000 || CLOCKLESS_FREQUENCY == 24000000) // || CLOCKLESS_FREQUENCY == 48000000 || CLOCKLESS_FREQUENCY == 96000000) // 125ns/clock
#define FMUL (CLOCKLESS_FREQUENCY/8000000)
// GE8822
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class GE8822Controller800Khz : public ClocklessController<DATA_PIN, 3 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER, 4> {};
// LPD1886
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class LPD1886Controller1250Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 3 * FMUL, 2 * FMUL, RGB_ORDER, 4> {};
// LPD1886
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class LPD1886Controller1250Khz_8bit : public ClocklessController<DATA_PIN, 2 * FMUL, 3 * FMUL, 2 * FMUL, RGB_ORDER> {};
// WS2811@800khz 2 clocks, 5 clocks, 3 clocks
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2812Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2811Controller800Khz : public ClocklessController<DATA_PIN, 3 * FMUL, 4 * FMUL, 3 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> //not tested
class WS2813Controller : public ClocklessController<DATA_PIN, 3 * FMUL, 4 * FMUL, 3 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2811Controller400Khz : public ClocklessController<DATA_PIN, 4 * FMUL, 10 * FMUL, 6 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class SK6822Controller : public ClocklessController<DATA_PIN, 3 * FMUL, 8 * FMUL, 3 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class SM16703Controller : public ClocklessController<DATA_PIN, 3 * FMUL, 4 * FMUL, 3 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class SK6812Controller : public ClocklessController<DATA_PIN, 3 * FMUL, 3 * FMUL, 4 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903Controller400Khz : public ClocklessController<DATA_PIN, 4 * FMUL, 12 * FMUL, 4 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903BController800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 4 * FMUL, 4 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1904Controller800Khz : public ClocklessController<DATA_PIN, 3 * FMUL, 3 * FMUL, 4 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS2903Controller : public ClocklessController<DATA_PIN, 2 * FMUL, 6 * FMUL, 2 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1809Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1803Controller400Khz : public ClocklessController<DATA_PIN, 6 * FMUL, 9 * FMUL, 6 * FMUL, RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1829Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER, 0, true, 500> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
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 GW6205Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 4 * FMUL, 4 * FMUL, RGB_ORDER, 4> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class PL9823Controller : public ClocklessController<DATA_PIN, 3 * FMUL, 8 * FMUL, 3 * FMUL, RGB_ORDER> {};
#else
// Similar to NS() macro, this calculates the number of cycles for
// the clockless chipset (which may differ from CPU cycles)
#ifdef FASTLED_TEENSY4
// just use raw nanosecond values for the teensy4
#define C_NS(_NS) _NS
#else
#define C_NS(_NS) (((_NS * ((CLOCKLESS_FREQUENCY / 1000000L)) + 999)) / 1000)
#endif
// GE8822 - 350ns 660ns 350ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class GE8822Controller800Khz : public ClocklessController<DATA_PIN, C_NS(350), C_NS(660), C_NS(350), RGB_ORDER, 4> {};
// GW6205@400khz - 800ns, 800ns, 800ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class GW6205Controller400Khz : public ClocklessController<DATA_PIN, C_NS(800), C_NS(800), C_NS(800), RGB_ORDER, 4> {};
// GW6205@400khz - 400ns, 400ns, 400ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class GW6205Controller800Khz : public ClocklessController<DATA_PIN, C_NS(400), C_NS(400), C_NS(400), RGB_ORDER, 4> {};
// UCS1903 - 500ns, 1500ns, 500ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903Controller400Khz : public ClocklessController<DATA_PIN, C_NS(500), C_NS(1500), C_NS(500), RGB_ORDER> {};
// UCS1903B - 400ns, 450ns, 450ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903BController800Khz : public ClocklessController<DATA_PIN, C_NS(400), C_NS(450), C_NS(450), RGB_ORDER> {};
// UCS1904 - 400ns, 400ns, 450ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1904Controller800Khz : public ClocklessController<DATA_PIN, C_NS(400), C_NS(400), C_NS(450), RGB_ORDER> {};
// UCS2903 - 250ns, 750ns, 250ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS2903Controller : public ClocklessController<DATA_PIN, C_NS(250), C_NS(750), C_NS(250), RGB_ORDER> {};
// TM1809 - 350ns, 350ns, 550ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1809Controller800Khz : public ClocklessController<DATA_PIN, C_NS(350), C_NS(350), C_NS(450), RGB_ORDER> {};
// WS2811 - 320ns, 320ns, 640ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2811Controller800Khz : public ClocklessController<DATA_PIN, C_NS(320), C_NS(320), C_NS(640), RGB_ORDER> {};
// WS2813 - 320ns, 320ns, 640ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2813Controller : public ClocklessController<DATA_PIN, C_NS(320), C_NS(320), C_NS(640), RGB_ORDER> {};
// WS2812 - 250ns, 625ns, 375ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2812Controller800Khz : public ClocklessController<DATA_PIN, C_NS(250), C_NS(625), C_NS(375), RGB_ORDER> {};
// WS2811@400khz - 800ns, 800ns, 900ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2811Controller400Khz : public ClocklessController<DATA_PIN, C_NS(800), C_NS(800), C_NS(900), RGB_ORDER> {};
// 750NS, 750NS, 750NS
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1803Controller400Khz : public ClocklessController<DATA_PIN, C_NS(700), C_NS(1100), C_NS(700), RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1829Controller800Khz : public ClocklessController<DATA_PIN, C_NS(340), C_NS(340), C_NS(550), RGB_ORDER, 0, true, 500> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1829Controller1600Khz : public ClocklessController<DATA_PIN, C_NS(100), C_NS(300), C_NS(200), RGB_ORDER, 0, true, 500> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class LPD1886Controller1250Khz : public ClocklessController<DATA_PIN, C_NS(200), C_NS(400), C_NS(200), RGB_ORDER, 4> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class LPD1886Controller1250Khz_8bit : public ClocklessController<DATA_PIN, C_NS(200), C_NS(400), C_NS(200), RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class SK6822Controller : public ClocklessController<DATA_PIN, C_NS(375), C_NS(1000), C_NS(375), RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class SK6812Controller : public ClocklessController<DATA_PIN, C_NS(300), C_NS(300), C_NS(600), RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class SM16703Controller : public ClocklessController<DATA_PIN, C_NS(300), C_NS(600), C_NS(300), RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class PL9823Controller : public ClocklessController<DATA_PIN, C_NS(350), C_NS(1010), C_NS(350), RGB_ORDER> {};
#endif
///@}
#endif
///@}
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,84 @@
#ifndef __INC_COLOR_H
#define __INC_COLOR_H
#include "FastLED.h"
FASTLED_NAMESPACE_BEGIN
///@file color.h
/// contains definitions for color correction and temperature
///@defgroup ColorEnums Color correction/temperature
/// definitions for color correction and light temperatures
///@{
typedef enum {
// Color correction starting points
/// typical values for SMD5050 LEDs
///@{
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 {
/// @name Black-body radiation light sources
/// Black-body radiation light sources emit a (relatively) continuous
/// spectrum, and can be described as having a Kelvin 'temperature'
///@{
/// 1900 Kelvin
Candle=0xFF9329 /* 1900 K, 255, 147, 41 */,
/// 2600 Kelvin
Tungsten40W=0xFFC58F /* 2600 K, 255, 197, 143 */,
/// 2850 Kelvin
Tungsten100W=0xFFD6AA /* 2850 K, 255, 214, 170 */,
/// 3200 Kelvin
Halogen=0xFFF1E0 /* 3200 K, 255, 241, 224 */,
/// 5200 Kelvin
CarbonArc=0xFFFAF4 /* 5200 K, 255, 250, 244 */,
/// 5400 Kelvin
HighNoonSun=0xFFFFFB /* 5400 K, 255, 255, 251 */,
/// 6000 Kelvin
DirectSunlight=0xFFFFFF /* 6000 K, 255, 255, 255 */,
/// 7000 Kelvin
OvercastSky=0xC9E2FF /* 7000 K, 201, 226, 255 */,
/// 20000 Kelvin
ClearBlueSky=0x409CFF /* 20000 K, 64, 156, 255 */,
///@}
/// @name Gaseous light sources
/// Gaseous light sources emit discrete spectral bands, and while we can
/// approximate their aggregate hue with RGB values, they don't actually
/// have a proper Kelvin temperature.
///@{
WarmFluorescent=0xFFF4E5 /* 0 K, 255, 244, 229 */,
StandardFluorescent=0xF4FFFA /* 0 K, 244, 255, 250 */,
CoolWhiteFluorescent=0xD4EBFF /* 0 K, 212, 235, 255 */,
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;
FASTLED_NAMESPACE_END
///@}
#endif

View File

@@ -0,0 +1,174 @@
#ifndef __INC_COLORPALETTES_H
#define __INC_COLORPALETTES_H
#define FASTLED_INTERNAL
#include "FastLED.h"
#include "colorutils.h"
#include "colorpalettes.h"
FASTLED_USING_NAMESPACE
// 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 FL_PROGMEM =
{
CRGB::Blue,
CRGB::DarkBlue,
CRGB::DarkBlue,
CRGB::DarkBlue,
CRGB::DarkBlue,
CRGB::DarkBlue,
CRGB::DarkBlue,
CRGB::DarkBlue,
CRGB::Blue,
CRGB::DarkBlue,
CRGB::SkyBlue,
CRGB::SkyBlue,
CRGB::LightBlue,
CRGB::White,
CRGB::LightBlue,
CRGB::SkyBlue
};
extern const TProgmemRGBPalette16 LavaColors_p FL_PROGMEM =
{
CRGB::Black,
CRGB::Maroon,
CRGB::Black,
CRGB::Maroon,
CRGB::DarkRed,
CRGB::Maroon,
CRGB::DarkRed,
CRGB::DarkRed,
CRGB::DarkRed,
CRGB::Red,
CRGB::Orange,
CRGB::White,
CRGB::Orange,
CRGB::Red,
CRGB::DarkRed
};
extern const TProgmemRGBPalette16 OceanColors_p FL_PROGMEM =
{
CRGB::MidnightBlue,
CRGB::DarkBlue,
CRGB::MidnightBlue,
CRGB::Navy,
CRGB::DarkBlue,
CRGB::MediumBlue,
CRGB::SeaGreen,
CRGB::Teal,
CRGB::CadetBlue,
CRGB::Blue,
CRGB::DarkCyan,
CRGB::CornflowerBlue,
CRGB::Aquamarine,
CRGB::SeaGreen,
CRGB::Aqua,
CRGB::LightSkyBlue
};
extern const TProgmemRGBPalette16 ForestColors_p FL_PROGMEM =
{
CRGB::DarkGreen,
CRGB::DarkGreen,
CRGB::DarkOliveGreen,
CRGB::DarkGreen,
CRGB::Green,
CRGB::ForestGreen,
CRGB::OliveDrab,
CRGB::Green,
CRGB::SeaGreen,
CRGB::MediumAquamarine,
CRGB::LimeGreen,
CRGB::YellowGreen,
CRGB::LightGreen,
CRGB::LawnGreen,
CRGB::MediumAquamarine,
CRGB::ForestGreen
};
/// HSV Rainbow
extern const TProgmemRGBPalette16 RainbowColors_p FL_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 FL_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 FL_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 FL_PROGMEM =
{
0x000000,
0x330000, 0x660000, 0x990000, 0xCC0000, 0xFF0000,
0xFF3300, 0xFF6600, 0xFF9900, 0xFFCC00, 0xFFFF00,
0xFFFF33, 0xFFFF66, 0xFFFF99, 0xFFFFCC, 0xFFFFFF
};
// Gradient palette "Rainbow_gp",
// provided for situations where you're going
// to use a number of other gradient palettes, AND
// you want a 'standard' FastLED rainbow as well.
DEFINE_GRADIENT_PALETTE( Rainbow_gp ) {
0, 255, 0, 0, // Red
32, 171, 85, 0, // Orange
64, 171, 171, 0, // Yellow
96, 0, 255, 0, // Green
128, 0, 171, 85, // Aqua
160, 0, 0, 255, // Blue
192, 85, 0, 171, // Purple
224, 171, 0, 85, // Pink
255, 255, 0, 0};// and back to Red
#endif

View File

@@ -0,0 +1,57 @@
#ifndef __INC_COLORPALETTES_H
#define __INC_COLORPALETTES_H
#include "FastLED.h"
#include "colorutils.h"
///@file colorpalettes.h
/// contains definitions for the predefined color palettes supplied by FastLED.
FASTLED_NAMESPACE_BEGIN
///@defgroup Colorpalletes Pre-defined color palletes
/// These schemes are all declared as "PROGMEM", meaning
/// that they won't take up SRAM on AVR chips until used.
/// Furthermore, the compiler won't even include these
/// in your PROGMEM (flash) storage unless you specifically
/// use each one, so you only 'pay for' those you actually use.
///@{
/// Cloudy color pallete
extern const TProgmemRGBPalette16 CloudColors_p FL_PROGMEM;
/// Lava colors
extern const TProgmemRGBPalette16 LavaColors_p FL_PROGMEM;
/// Ocean colors, blues and whites
extern const TProgmemRGBPalette16 OceanColors_p FL_PROGMEM;
/// Forest colors, greens
extern const TProgmemRGBPalette16 ForestColors_p FL_PROGMEM;
/// HSV Rainbow
extern const TProgmemRGBPalette16 RainbowColors_p FL_PROGMEM;
#define RainbowStripesColors_p RainbowStripeColors_p
/// HSV Rainbow colors with alternatating stripes of black
extern const TProgmemRGBPalette16 RainbowStripeColors_p FL_PROGMEM;
/// HSV color ramp: blue purple ping red orange yellow (and back)
/// Basically, everything but the greens, which tend to make
/// people's skin look unhealthy. This palette is good for
/// lighting at a club or party, where it'll be shining on people.
extern const TProgmemRGBPalette16 PartyColors_p FL_PROGMEM;
/// Approximate "black body radiation" palette, akin to
/// the FastLED 'HeatColor' function.
/// Recommend that you use values 0-240 rather than
/// the usual 0-255, as the last 15 colors will be
/// 'wrapping around' from the hot end to the cold end,
/// which looks wrong.
extern const TProgmemRGBPalette16 HeatColors_p FL_PROGMEM;
DECLARE_GRADIENT_PALETTE( Rainbow_gp);
FASTLED_NAMESPACE_END
///@}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,418 @@
#ifndef __INC_CONTROLLER_H
#define __INC_CONTROLLER_H
///@file controller.h
/// base definitions used by led controllers for writing out led data
#include "FastLED.h"
#include "led_sysdefs.h"
#include "pixeltypes.h"
#include "color.h"
#include <stddef.h>
FASTLED_NAMESPACE_BEGIN
#define RO(X) RGB_BYTE(RGB_ORDER, X)
#define RGB_BYTE(RO,X) (((RO)>>(3*(2-(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.
/// 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 {
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
///@param data the crgb color to set the leds to
///@param nLeds the number of leds to set to this color
///@param scale the rgb scaling value for outputting color
virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) = 0;
/// write the passed in rgb data out to the leds managed by this controller
///@param data the rgb data to write out to the strip
///@param nLeds the number of leds being written out
///@param scale the rgb scaling to apply to each led before writing it out
virtual void show(const struct CRGB *data, int nLeds, CRGB scale) = 0;
public:
/// create an led controller object, add it to the chain of controllers
CLEDController() : m_Data(NULL), m_ColorCorrection(UncorrectedColor), m_ColorTemperature(UncorrectedTemperature), m_DitherMode(BINARY_DITHER), m_nLeds(0) {
m_pNext = NULL;
if(m_pHead==NULL) { m_pHead = this; }
if(m_pTail != NULL) { m_pTail->m_pNext = this; }
m_pTail = this;
}
///initialize the LED controller
virtual void init() = 0;
///clear out/zero out the given number of leds.
virtual void clearLeds(int nLeds) { showColor(CRGB::Black, nLeds, CRGB::Black); }
/// 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));
}
/// show the given color on the led strip
void showColor(const struct CRGB & data, uint8_t brightness=255) {
showColor(data, m_nLeds, getAdjustment(brightness));
}
/// get the first led controller in the chain of controllers
static CLEDController *head() { return m_pHead; }
/// get the next controller in the chain after this one. will return NULL at the end of the chain
CLEDController *next() { return m_pNext; }
/// set the default array of leds to be used by this controller
CLEDController & setLeds(CRGB *data, int nLeds) {
m_Data = data;
m_nLeds = nLeds;
return *this;
}
/// zero out the led data managed by this controller
void clearLedData() {
if(m_Data) {
memset8((void*)m_Data, 0, sizeof(struct CRGB) * m_nLeds);
}
}
/// How many leds does this controller manage?
virtual 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]; }
/// set the dithering mode for this controller to use
inline CLEDController & setDither(uint8_t ditherMode = BINARY_DITHER) { m_DitherMode = ditherMode; return *this; }
/// get the dithering option currently set for this controller
inline uint8_t getDither() { return m_DitherMode; }
/// the the color corrction to use for this controller, expressed as an rgb object
CLEDController & setCorrection(CRGB correction) { m_ColorCorrection = correction; return *this; }
/// set the color correction to use for this controller
CLEDController & setCorrection(LEDColorCorrection correction) { m_ColorCorrection = correction; return *this; }
/// get the correction value used by this controller
CRGB getCorrection() { return m_ColorCorrection; }
/// set the color temperature, aka white point, for this controller
CLEDController & setTemperature(CRGB temperature) { m_ColorTemperature = temperature; return *this; }
/// set the color temperature, aka white point, for this controller
CLEDController & setTemperature(ColorTemperature temperature) { m_ColorTemperature = temperature; return *this; }
/// get the color temperature, aka whipe point, for this controller
CRGB getTemperature() { return m_ColorTemperature; }
/// Get the combined brightness/color adjustment for this controller
CRGB getAdjustment(uint8_t scale) {
return computeAdjustment(scale, m_ColorCorrection, m_ColorTemperature);
}
static CRGB computeAdjustment(uint8_t scale, const CRGB & colorCorrection, const CRGB & colorTemperature) {
#if defined(NO_CORRECTION) && (NO_CORRECTION==1)
return CRGB(scale,scale,scale);
#else
CRGB adj(0,0,0);
if(scale > 0) {
for(uint8_t i = 0; i < 3; ++i) {
uint8_t cc = colorCorrection.raw[i];
uint8_t ct = colorTemperature.raw[i];
if(cc > 0 && ct > 0) {
uint32_t work = (((uint32_t)cc)+1) * (((uint32_t)ct)+1) * scale;
work /= 0x10000L;
adj.raw[i] = work & 0xFF;
}
}
}
return adj;
#endif
}
virtual uint16_t getMaxRefreshRate() const { return 0; }
};
// Pixel controller class. This is the class that we use to centralize pixel access in a block of data, including
// support for things like RGB reordering, scaling, dithering, skipping (for ARGB data), and eventually, we will
// centralize 8/12/16 conversions here as well.
template<EOrder RGB_ORDER, int LANES=1, uint32_t MASK=0xFFFFFFFF>
struct PixelController {
const uint8_t *mData;
int mLen,mLenRemaining;
uint8_t d[3];
uint8_t e[3];
CRGB mScale;
int8_t mAdvance;
int mOffsets[LANES];
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;
mLenRemaining = mLen = other.mLen;
for(int i = 0; i < LANES; ++i) { mOffsets[i] = other.mOffsets[i]; }
}
void initOffsets(int len) {
int nOffset = 0;
for(int i = 0; i < LANES; ++i) {
mOffsets[i] = nOffset;
if((1<<i) & MASK) { nOffset += (len * mAdvance); }
}
}
PixelController(const uint8_t *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER, bool advance=true, uint8_t skip=0) : mData(d), mLen(len), mLenRemaining(len), mScale(s) {
enable_dithering(dither);
mData += skip;
mAdvance = (advance) ? 3+skip : 0;
initOffsets(len);
}
PixelController(const CRGB *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)d), mLen(len), mLenRemaining(len), mScale(s) {
enable_dithering(dither);
mAdvance = 3;
initOffsets(len);
}
PixelController(const CRGB &d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)&d), mLen(len), mLenRemaining(len), mScale(s) {
enable_dithering(dither);
mAdvance = 0;
initOffsets(len);
}
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 uint8_t R = 0;
++R;
// R is wrapped around at 2^ditherBits,
// so if ditherBits is 2, R will cycle through (0,1,2,3)
uint8_t 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)
uint8_t 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) {
uint8_t s = mScale.raw[i];
e[i] = s ? (256/s) + 1 : 0;
d[i] = scale8(Q, e[i]);
#if (FASTLED_SCALE8_FIXED == 1)
if(d[i]) (--d[i]);
#endif
if(e[i]) --e[i];
}
#endif
}
// Do we have n pixels left to process?
__attribute__((always_inline)) inline bool has(int n) {
return mLenRemaining >= 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;
}
}
__attribute__((always_inline)) inline int size() { return mLen; }
// 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; --mLenRemaining;}
// 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 loadByte(PixelController & pc, int lane) { return pc.mData[pc.mOffsets[lane] + 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 dither(PixelController & , uint8_t b, uint8_t d) { return b ? qadd8(b,d) : 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)]); }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t scale(PixelController & , uint8_t b, uint8_t scale) { return scale8(b, scale); }
// composite shortcut functions for loading, dithering, and scaling
template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc) { return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc))); }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane) { return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc, lane))); }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t d, uint8_t scale) { return scale8(pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc, lane), d), scale); }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t scale) { return scale8(pc.loadByte<SLOT>(pc, lane), scale); }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc); }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc, lane); }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane, uint8_t scale) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc, lane, scale); }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t getd(PixelController & pc) { return pc.d[RO(SLOT)]; }
template<int SLOT> __attribute__((always_inline)) inline static uint8_t getscale(PixelController & pc) { return pc.mScale.raw[RO(SLOT)]; }
// Helper functions to get around gcc stupidities
__attribute__((always_inline)) inline uint8_t loadAndScale0(int lane, uint8_t scale) { return loadAndScale<0>(*this, lane, scale); }
__attribute__((always_inline)) inline uint8_t loadAndScale1(int lane, uint8_t scale) { return loadAndScale<1>(*this, lane, scale); }
__attribute__((always_inline)) inline uint8_t loadAndScale2(int lane, uint8_t scale) { return loadAndScale<2>(*this, lane, scale); }
__attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane, uint8_t scale) { return advanceAndLoadAndScale<0>(*this, lane, scale); }
__attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane, uint8_t scale) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane, scale); }
__attribute__((always_inline)) inline uint8_t loadAndScale0(int lane) { return loadAndScale<0>(*this, lane); }
__attribute__((always_inline)) inline uint8_t loadAndScale1(int lane) { return loadAndScale<1>(*this, lane); }
__attribute__((always_inline)) inline uint8_t loadAndScale2(int lane) { return loadAndScale<2>(*this, lane); }
__attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane) { return advanceAndLoadAndScale<0>(*this, lane); }
__attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane); }
__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); }
__attribute__((always_inline)) inline uint8_t getScale0() { return getscale<0>(*this); }
__attribute__((always_inline)) inline uint8_t getScale1() { return getscale<1>(*this); }
__attribute__((always_inline)) inline uint8_t getScale2() { return getscale<2>(*this); }
};
template<EOrder RGB_ORDER, int LANES=1, uint32_t MASK=0xFFFFFFFF> class CPixelLEDController : public CLEDController {
protected:
virtual void showPixels(PixelController<RGB_ORDER,LANES,MASK> & pixels) = 0;
/// set all the leds on the controller to a given color
///@param data the crgb color to set the leds to
///@param nLeds the numner of leds to set to this color
///@param scale the rgb scaling value for outputting color
virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) {
PixelController<RGB_ORDER, LANES, MASK> pixels(data, nLeds, scale, getDither());
showPixels(pixels);
}
/// write the passed in rgb data out to the leds managed by this controller
///@param data the rgb data to write out to the strip
///@param nLeds the number of leds being written out
///@param scale the rgb scaling to apply to each led before writing it out
virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
PixelController<RGB_ORDER, LANES, MASK> pixels(data, nLeds, scale, getDither());
showPixels(pixels);
}
public:
CPixelLEDController() : CLEDController() {}
};
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,16 @@
#ifndef __INC_CPP_COMPAT_H
#define __INC_CPP_COMPAT_H
#include "FastLED.h"
#if __cplusplus <= 199711L
#define static_assert(expression, message)
#define constexpr const
#else
// things that we can turn on if we're in a C++11 environment
#endif
#endif

View File

@@ -0,0 +1,65 @@
#ifndef __INC_DMX_H
#define __INC_DMX_H
#include "FastLED.h"
#ifdef DmxSimple_h
#include <DmxSimple.h>
#define HAS_DMX_SIMPLE
///@ingroup chipsets
///@{
FASTLED_NAMESPACE_BEGIN
// note - dmx simple must be included before FastSPI for this code to be enabled
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class DMXSimpleController : public CPixelLEDController<RGB_ORDER> {
public:
// initialize the LED controller
virtual void init() { DmxSimple.usePin(DATA_PIN); }
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
int iChannel = 1;
while(pixels.has(1)) {
DmxSimple.write(iChannel++, pixels.loadAndScale0());
DmxSimple.write(iChannel++, pixels.loadAndScale1());
DmxSimple.write(iChannel++, pixels.loadAndScale2());
pixels.advanceData();
pixels.stepDithering();
}
}
};
FASTLED_NAMESPACE_END
#endif
#ifdef DmxSerial_h
#include <DMXSerial.h>
FASTLED_NAMESPACE_BEGIN
template <EOrder RGB_ORDER = RGB> class DMXSerialController : public CPixelLEDController<RGB_ORDER> {
public:
// initialize the LED controller
virtual void init() { DMXSerial.init(DMXController); }
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
int iChannel = 1;
while(pixels.has(1)) {
DMXSerial.write(iChannel++, pixels.loadAndScale0());
DMXSerial.write(iChannel++, pixels.loadAndScale1());
DMXSerial.write(iChannel++, pixels.loadAndScale2());
pixels.advanceData();
pixels.stepDithering();
}
}
};
FASTLED_NAMESPACE_END
///@}
#define HAS_DMX_SERIAL
#endif
#endif

View File

@@ -0,0 +1,69 @@
#ifndef __INC_FASTLED_CONFIG_H
#define __INC_FASTLED_CONFIG_H
#include "FastLED.h"
///@file fastled_config.h
/// contains definitions that can be used to configure FastLED at compile time
// Use this option only for debugging pin access and forcing software pin access. Note that
// software pin access only works in Arduino based environments. Forces use of digitalWrite
// methods for pin access vs. direct hardware port access
// #define FASTLED_FORCE_SOFTWARE_PINS
// Use this option only for debugging bitbang'd spi access or to work around bugs in hardware
// spi access. Forces use of bit-banged spi, even on pins that has hardware SPI available.
// #define FASTLED_FORCE_SOFTWARE_SPI
// Use this to force FastLED to allow interrupts in the clockless chipsets (or to force it to
// disallow), overriding the default on platforms that support this. Set the value to 1 to
// allow interrupts or 0 to disallow them.
// #define FASTLED_ALLOW_INTERRUPTS 1
// #define FASTLED_ALLOW_INTERRUPTS 0
// Use this to allow some integer overflows/underflows in the inoise functions.
// The original implementions allowed this, and had some discontinuties in the noise
// output. It's technically an implementation bug, and was fixed, but you may wish
// to preserve the old look and feel of the inoise functions in your existing animations.
// The default is 0: NO overflow, and 'continuous' noise output, aka the fixed way.
// #define FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW 0
// #define FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW 1
// Use this toggle whether or not to use the 'fixed' FastLED scale8. The initial scale8
// had a problem where scale8(255,255) would give you 254. This is now fixed, and that
// fix is enabled by default. However, if for some reason you have code that is not
// working right as a result of this (e.g. code that was expecting the old scale8 behavior)
// you can disable it here.
#define FASTLED_SCALE8_FIXED 1
// #define FASTLED_SCALE8_FIXED 0
// Use this toggle whether to use 'fixed' FastLED pixel blending, including ColorFromPalette.
// The prior pixel blend functions had integer-rounding math errors that led to
// small errors being inadvertently added to the low bits of blended colors, including colors
// retrieved from color palettes using LINEAR_BLEND. This is now fixed, and the
// fix is enabled by default. However, if for some reason you wish to run with the old
// blending, including the integer rounding and color errors, you can disable the bugfix here.
#define FASTLED_BLEND_FIXED 1
// #define FASTLED_BLEND_FIXED 0
// Use this toggle whether to use 'fixed' FastLED 8- and 16-bit noise functions.
// The prior noise functions had some math errors that led to 'discontinuities' in the
// output, which by definition should be smooth and continuous. The bug led to
// noise function output that had 'edges' and glitches in it. This is now fixed, and the
// fix is enabled by default. However, if for some reason you wish to run with the old
// noise code, including the glitches, you can disable the bugfix here.
#define FASTLED_NOISE_FIXED 1
//#define FASTLED_NOISE_FIXED 0
// Use this to determine how many times FastLED will attempt to re-transmit a frame if interrupted
// for too long by interrupts.
#ifndef FASTLED_INTERRUPT_RETRY_COUNT
#define FASTLED_INTERRUPT_RETRY_COUNT 2
#endif
// Use this toggle to enable global brightness in contollers that support is (ADA102 and SK9822).
// It changes how color scaling works and uses global brightness before scaling down color values.
// This enable much more accurate color control on low brightness settings.
//#define FASTLED_USE_GLOBAL_BRIGHTNESS 1
#endif

View File

@@ -0,0 +1,140 @@
#ifndef __INC_FL_DELAY_H
#define __INC_FL_DELAY_H
#include "FastLED.h"
///@file fastled_delay.h
///Utility functions and classes for managing delaycycles
FASTLED_NAMESPACE_BEGIN
/// Class to ensure that a minimum amount of time has kicked since the last time run - and delay if not enough time has passed yet
/// this should make sure that chipsets that have
template<int WAIT> class CMinWait {
uint16_t mLastMicros;
public:
CMinWait() { mLastMicros = 0; }
void wait() {
uint16_t diff;
do {
diff = (micros() & 0xFFFF) - mLastMicros;
} while(diff < WAIT);
}
void mark() { mLastMicros = micros() & 0xFFFF; }
};
////////////////////////////////////////////////////////////////////////////////////////////
//
// Clock cycle counted delay loop
//
////////////////////////////////////////////////////////////////////////////////////////////
// Default is now just 'nop', with special case for AVR
// ESP32 core has it's own definition of NOP, so undef it first
#ifdef ESP32
#undef NOP
#undef NOP2
#endif
#if defined(__AVR__)
# define FL_NOP __asm__ __volatile__ ("cp r0,r0\n");
# define FL_NOP2 __asm__ __volatile__ ("rjmp .+0");
#else
# define FL_NOP __asm__ __volatile__ ("nop\n");
# define FL_NOP2 __asm__ __volatile__ ("nop\n\t nop\n");
#endif
// predeclaration to not upset the compiler
template<int CYCLES> inline void delaycycles();
template<int CYCLES> inline void delaycycles_min1() {
delaycycles<1>();
delaycycles<CYCLES-1>();
}
// 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>();
// 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__ (
" LDI R16, %0\n"
"L_%=: DEC R16\n"
" BRNE L_%=\n"
: /* no outputs */
: "M" (LOOP)
: "r16"
);
}
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>();
FL_NOP; delaycycles<CYCLES-1>();
}
#endif
// pre-instantiations for values small enough to not need the loop, as well as sanity holders
// for some negative values.
template<> __attribute__((always_inline)) inline void delaycycles<-10>() {}
template<> __attribute__((always_inline)) inline void delaycycles<-9>() {}
template<> __attribute__((always_inline)) inline void delaycycles<-8>() {}
template<> __attribute__((always_inline)) inline void delaycycles<-7>() {}
template<> __attribute__((always_inline)) inline void delaycycles<-6>() {}
template<> __attribute__((always_inline)) inline void delaycycles<-5>() {}
template<> __attribute__((always_inline)) inline void delaycycles<-4>() {}
template<> __attribute__((always_inline)) inline void delaycycles<-3>() {}
template<> __attribute__((always_inline)) inline void delaycycles<-2>() {}
template<> __attribute__((always_inline)) inline void delaycycles<-1>() {}
template<> __attribute__((always_inline)) inline void delaycycles<0>() {}
template<> __attribute__((always_inline)) inline void delaycycles<1>() {FL_NOP;}
template<> __attribute__((always_inline)) inline void delaycycles<2>() {FL_NOP2;}
template<> __attribute__((always_inline)) inline void delaycycles<3>() {FL_NOP;FL_NOP2;}
template<> __attribute__((always_inline)) inline void delaycycles<4>() {FL_NOP2;FL_NOP2;}
template<> __attribute__((always_inline)) inline void delaycycles<5>() {FL_NOP2;FL_NOP2;FL_NOP;}
// Some timing related macros/definitions
// Macro to convert from nano-seconds to clocks and clocks to nano-seconds
// #define NS(_NS) (_NS / (1000 / (F_CPU / 1000000L)))
#define F_CPU_MHZ (F_CPU / 1000000L)
// #define NS(_NS) ( (_NS * (F_CPU / 1000000L))) / 1000
#define NS(_NS) (((_NS * F_CPU_MHZ) + 999) / 1000)
#define CLKS_TO_MICROS(_CLKS) ((long)(_CLKS)) / (F_CPU / 1000000L)
// Macro for making sure there's enough time available
#define NO_TIME(A, B, C) (NS(A) < 3 || NS(B) < 3 || NS(C) < 6)
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,81 @@
#ifndef __INC_FL_PROGMEM_H
#define __INC_FL_PROGMEM_H
#include "FastLED.h"
///@file fastled_progmem.h
/// wrapper definitions to allow seamless use of PROGMEM in environmens that have it
FASTLED_NAMESPACE_BEGIN
// Compatibility layer for devices that do or don't
// have "PROGMEM" and the associated pgm_ accessors.
//
// If a platform supports PROGMEM, it should define
// "FASTLED_USE_PROGMEM" as 1, otherwise FastLED will
// fall back to NOT using PROGMEM.
//
// Whether or not pgmspace.h is #included is separately
// controllable by FASTLED_INCLUDE_PGMSPACE, if needed.
// If FASTLED_USE_PROGMEM is 1, we'll map FL_PROGMEM
// and the FL_PGM_* accessors to the Arduino equivalents.
#if FASTLED_USE_PROGMEM == 1
#ifndef FASTLED_INCLUDE_PGMSPACE
#define FASTLED_INCLUDE_PGMSPACE 1
#endif
#if FASTLED_INCLUDE_PGMSPACE == 1
#include <avr/pgmspace.h>
#endif
#define FL_PROGMEM PROGMEM
// Note: only the 'near' memory wrappers are provided.
// If you're using 'far' memory, you already have
// portability issues to work through, but you could
// add more support here if needed.
#define FL_PGM_READ_BYTE_NEAR(x) (pgm_read_byte_near(x))
#define FL_PGM_READ_WORD_NEAR(x) (pgm_read_word_near(x))
#define FL_PGM_READ_DWORD_NEAR(x) (pgm_read_dword_near(x))
// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734
#if __GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 6))
#ifdef FASTLED_AVR
#ifdef PROGMEM
#undef PROGMEM
#define PROGMEM __attribute__((section(".progmem.data")))
#endif
#endif
#endif
#else
// If FASTLED_USE_PROGMEM is 0 or undefined,
// we'll use regular memory (RAM) access.
//empty PROGMEM simulation
#define FL_PROGMEM
#define FL_PGM_READ_BYTE_NEAR(x) (*((const uint8_t*)(x)))
#define FL_PGM_READ_WORD_NEAR(x) (*((const uint16_t*)(x)))
#define FL_PGM_READ_DWORD_NEAR(x) (*((const uint32_t*)(x)))
#endif
// On some platforms, most notably ARM M0, unaligned access
// to 'PROGMEM' for multibyte values (eg read dword) is
// not allowed and causes a crash. This macro can help
// force 4-byte alignment as needed. The FastLED gradient
// palette code uses 'read dword', and now uses this macro
// to make sure that gradient palettes are 4-byte aligned.
#if defined(FASTLED_ARM) || defined(ESP32) || defined(ESP8266)
#define FL_ALIGN_PROGMEM __attribute__ ((aligned (4)))
#else
#define FL_ALIGN_PROGMEM
#endif
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,275 @@
#ifndef __INC_FASTPIN_H
#define __INC_FASTPIN_H
#include "FastLED.h"
#include "led_sysdefs.h"
#include <stddef.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
///@file fastpin.h
/// Class base definitions for defining fast pin access
FASTLED_NAMESPACE_BEGIN
#define NO_PIN 255
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Pin access class - needs to tune for various platforms (naive fallback solution?)
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class Selectable {
public:
virtual void select() = 0;
virtual void release() = 0;
virtual bool isSelected() = 0;
};
#if !defined(FASTLED_NO_PINMAP)
class Pin : public Selectable {
volatile RwReg *mPort;
volatile RoReg *mInPort;
RwReg mPinMask;
uint8_t mPin;
void _init() {
mPinMask = digitalPinToBitMask(mPin);
mPort = (volatile RwReg*)portOutputRegister(digitalPinToPort(mPin));
mInPort = (volatile RoReg*)portInputRegister(digitalPinToPort(mPin));
}
public:
Pin(int pin) : mPin(pin) { _init(); }
typedef volatile RwReg * port_ptr_t;
typedef RwReg port_t;
inline void setOutput() { pinMode(mPin, OUTPUT); }
inline void setInput() { pinMode(mPin, INPUT); }
inline void hi() __attribute__ ((always_inline)) { *mPort |= mPinMask; }
inline void lo() __attribute__ ((always_inline)) { *mPort &= ~mPinMask; }
inline void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline void toggle() __attribute__ ((always_inline)) { *mInPort = mPinMask; }
inline void hi(register port_ptr_t port) __attribute__ ((always_inline)) { *port |= mPinMask; }
inline void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~mPinMask; }
inline void set(register port_t val) __attribute__ ((always_inline)) { *mPort = val; }
inline void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
port_t hival() __attribute__ ((always_inline)) { return *mPort | mPinMask; }
port_t loval() __attribute__ ((always_inline)) { return *mPort & ~mPinMask; }
port_ptr_t port() __attribute__ ((always_inline)) { return mPort; }
port_t mask() __attribute__ ((always_inline)) { return mPinMask; }
virtual void select() { hi(); }
virtual void release() { lo(); }
virtual bool isSelected() { return (*mPort & mPinMask) == mPinMask; }
};
class OutputPin : public Pin {
public:
OutputPin(int pin) : Pin(pin) { setOutput(); }
};
class InputPin : public Pin {
public:
InputPin(int pin) : Pin(pin) { setInput(); }
};
#else
// This is the empty code version of the raw pin class, method bodies should be filled in to Do The Right Thing[tm] when making this
// available on a new platform
class Pin : public Selectable {
volatile RwReg *mPort;
volatile RoReg *mInPort;
RwReg mPinMask;
uint8_t mPin;
void _init() {
// TODO: fill in init on a new platform
mPinMask = 0;
mPort = NULL;
mInPort = NULL;
}
public:
Pin(int pin) : mPin(pin) { _init(); }
void setPin(int pin) { mPin = pin; _init(); }
typedef volatile RwReg * port_ptr_t;
typedef RwReg port_t;
inline void setOutput() { /* TODO: Set pin output */ }
inline void setInput() { /* TODO: Set pin input */ }
inline void hi() __attribute__ ((always_inline)) { *mPort |= mPinMask; }
inline void lo() __attribute__ ((always_inline)) { *mPort &= ~mPinMask; }
inline void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline void toggle() __attribute__ ((always_inline)) { *mInPort = mPinMask; }
inline void hi(register port_ptr_t port) __attribute__ ((always_inline)) { *port |= mPinMask; }
inline void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~mPinMask; }
inline void set(register port_t val) __attribute__ ((always_inline)) { *mPort = val; }
inline void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
port_t hival() __attribute__ ((always_inline)) { return *mPort | mPinMask; }
port_t loval() __attribute__ ((always_inline)) { return *mPort & ~mPinMask; }
port_ptr_t port() __attribute__ ((always_inline)) { return mPort; }
port_t mask() __attribute__ ((always_inline)) { return mPinMask; }
virtual void select() { hi(); }
virtual void release() { lo(); }
virtual bool isSelected() { return (*mPort & mPinMask) == mPinMask; }
};
class OutputPin : public Pin {
public:
OutputPin(int pin) : Pin(pin) { setOutput(); }
};
class InputPin : public Pin {
public:
InputPin(int pin) : Pin(pin) { setInput(); }
};
#endif
/// The simplest level of Pin class. This relies on runtime functions durinig initialization to get the port/pin mask for the pin. Most
/// of the accesses involve references to these static globals that get set up. This won't be the fastest set of pin operations, but it
/// will provide pin level access on pretty much all arduino environments. In addition, it includes some methods to help optimize access in
/// various ways. Namely, the versions of hi, lo, and fastset that take the port register as a passed in register variable (saving a global
/// dereference), since these functions are aggressively inlined, that can help collapse out a lot of extraneous memory loads/dereferences.
///
/// In addition, if, while writing a bunch of data to a pin, you know no other pins will be getting written to, you can get/cache a value of
/// the pin's port register and use that to do a full set to the register. This results in one being able to simply do a store to the register,
/// vs. the load, and/or, and store that would be done normally.
///
/// There are platform specific instantiations of this class that provide direct i/o register access to pins for much higher speed pin twiddling.
///
/// Note that these classes are all static functions. So the proper usage is Pin<13>::hi(); or such. Instantiating objects is not recommended,
/// as passing Pin objects around will likely -not- have the effect you're expecting.
#ifdef FASTLED_FORCE_SOFTWARE_PINS
template<uint8_t PIN> class FastPin {
static RwReg sPinMask;
static volatile RwReg *sPort;
static volatile RoReg *sInPort;
static void _init() {
#if !defined(FASTLED_NO_PINMAP)
sPinMask = digitalPinToBitMask(PIN);
sPort = portOutputRegister(digitalPinToPort(PIN));
sInPort = portInputRegister(digitalPinToPort(PIN));
#endif
}
public:
typedef volatile RwReg * port_ptr_t;
typedef RwReg port_t;
inline static void setOutput() { _init(); pinMode(PIN, OUTPUT); }
inline static void setInput() { _init(); pinMode(PIN, INPUT); }
inline static void hi() __attribute__ ((always_inline)) { *sPort |= sPinMask; }
inline static void lo() __attribute__ ((always_inline)) { *sPort &= ~sPinMask; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline static void toggle() __attribute__ ((always_inline)) { *sInPort = sPinMask; }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { *port |= sPinMask; }
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~sPinMask; }
inline static void set(register port_t val) __attribute__ ((always_inline)) { *sPort = val; }
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
static port_t hival() __attribute__ ((always_inline)) { return *sPort | sPinMask; }
static port_t loval() __attribute__ ((always_inline)) { return *sPort & ~sPinMask; }
static port_ptr_t port() __attribute__ ((always_inline)) { return sPort; }
static port_t mask() __attribute__ ((always_inline)) { return sPinMask; }
};
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;
#else
template<uint8_t PIN> class FastPin {
constexpr static bool validpin() { return false; }
static_assert(validpin(), "Invalid pin specified");
static void _init() { }
public:
typedef volatile RwReg * port_ptr_t;
typedef RwReg port_t;
inline static void setOutput() { }
inline static void setInput() { }
inline static void hi() __attribute__ ((always_inline)) { }
inline static void lo() __attribute__ ((always_inline)) { }
inline static void strobe() __attribute__ ((always_inline)) { }
inline static void toggle() __attribute__ ((always_inline)) { }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { }
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { }
inline static void set(register port_t val) __attribute__ ((always_inline)) { }
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { }
static port_t hival() __attribute__ ((always_inline)) { return 0; }
static port_t loval() __attribute__ ((always_inline)) { return 0;}
static port_ptr_t port() __attribute__ ((always_inline)) { return NULL; }
static port_t mask() __attribute__ ((always_inline)) { return 0; }
};
#endif
template<uint8_t PIN> class FastPinBB : public FastPin<PIN> {};
typedef volatile uint32_t & reg32_t;
typedef volatile uint32_t * ptr_reg32_t;
// Utility templates for tracking down information about pins and ports
template<uint8_t port> struct __FL_PORT_INFO {
static bool hasPort() { return 0; }
static const char *portName() { return "--"; }
static const void *portAddr() { return NULL; }
};
// Give us our instantiations for defined ports - we're going to abuse this later for
// auto discovery of pin/port mappings for new variants. Use _FL_DEFINE_PORT for ports that
// are numeric in nature, e.g. GPIO0, GPIO1. Use _FL_DEFINE_PORT3 for ports that are letters.
// The first parameter will be the letter, the second parameter will be an integer/counter of smoe kind
// (this is because attempts to turn macro parameters into character constants break in some compilers)
#define _FL_DEFINE_PORT(L, BASE) template<> struct __FL_PORT_INFO<L> { \
static bool hasPort() { return 1; } \
static const char *portName() { return #L; } \
typedef BASE __t_baseType; \
static const void *portAddr() { return (void*)&__t_baseType::r(); } };
#define _FL_DEFINE_PORT3(L, LC, BASE) template<> struct __FL_PORT_INFO<LC> { \
static bool hasPort() { return 1; } \
static const char *portName() { return #L; } \
typedef BASE __t_baseType; \
static const void *portAddr() { return (void*)&__t_baseType::r(); } };
FASTLED_NAMESPACE_END
#pragma GCC diagnostic pop
#endif // __INC_FASTPIN_H

View File

@@ -0,0 +1,159 @@
#ifndef __INC_FASTSPI_H
#define __INC_FASTSPI_H
#include "FastLED.h"
#include "controller.h"
#include "lib8tion.h"
#include "fastspi_bitbang.h"
FASTLED_NAMESPACE_BEGIN
#if defined(FASTLED_TEENSY3) && (F_CPU > 48000000)
#define DATA_RATE_MHZ(X) (((48000000L / 1000000L) / X))
#define DATA_RATE_KHZ(X) (((48000000L / 1000L) / X))
#elif defined(FASTLED_TEENSY4) // && (ARM_HARDWARE_SPI)
// just use clocks
#define DATA_RATE_MHZ(X) (1000000 * (X))
#define DATA_RATE_KHZ(X) (1000 * (X))
#else
#define DATA_RATE_MHZ(X) ((F_CPU / 1000000L) / X)
#define DATA_RATE_KHZ(X) ((F_CPU / 1000L) / X)
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// External SPI template definition with partial instantiation(s) to map to hardware SPI ports on platforms/builds where the pin
// mappings are known at compile time.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if !defined(FASTLED_ALL_PINS_HARDWARE_SPI)
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class SPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
#endif
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class SoftwareSPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
#ifndef FASTLED_FORCE_SOFTWARE_SPI
#if defined(NRF51) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class SPIOutput : public NRF51SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
#endif
#if defined(NRF52_SERIES) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class SPIOutput : public NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
#endif
#if defined(FASTLED_APOLLO3) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class SPIOutput : public APOLLO3HardwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
#endif
#if defined(SPI_DATA) && defined(SPI_CLOCK)
#if defined(FASTLED_TEENSY3) && defined(ARM_HARDWARE_SPI)
template<uint32_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<uint32_t SPI_SPEED>
class SPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED, 0x4002C000> {};
template<uint32_t SPI_SPEED>
class SPIOutput<SPI_DATA, SPI2_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI_DATA, SPI2_CLOCK, SPI_SPEED, 0x4002C000> {};
template<uint32_t SPI_SPEED>
class SPIOutput<SPI2_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI2_DATA, SPI_CLOCK, SPI_SPEED, 0x4002C000> {};
#endif
#elif defined(FASTLED_TEENSY4) && defined(ARM_HARDWARE_SPI)
template<uint32_t SPI_SPEED>
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public Teesy4HardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED, SPI, 0> {};
template<uint32_t SPI_SPEED>
class SPIOutput<SPI1_DATA, SPI_CLOCK, SPI_SPEED> : public Teesy4HardwareSPIOutput<SPI1_DATA, SPI1_CLOCK, SPI_SPEED, SPI1, 1> {};
template<uint32_t SPI_SPEED>
class SPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED> : public Teesy4HardwareSPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED, SPI2, 2> {};
#elif defined(FASTLED_TEENSYLC) && defined(ARM_HARDWARE_SPI)
#define DECLARE_SPI0(__DATA,__CLOCK) template<uint32_t SPI_SPEED>\
class SPIOutput<__DATA, __CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<__DATA, __CLOCK, SPI_SPEED, 0x40076000> {};
#define DECLARE_SPI1(__DATA,__CLOCK) template<uint32_t SPI_SPEED>\
class SPIOutput<__DATA, __CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<__DATA, __CLOCK, SPI_SPEED, 0x40077000> {};
DECLARE_SPI0(7,13);
DECLARE_SPI0(8,13);
DECLARE_SPI0(11,13);
DECLARE_SPI0(12,13);
DECLARE_SPI0(7,14);
DECLARE_SPI0(8,14);
DECLARE_SPI0(11,14);
DECLARE_SPI0(12,14);
DECLARE_SPI1(0,20);
DECLARE_SPI1(1,20);
DECLARE_SPI1(21,20);
#elif defined(__SAM3X8E__)
template<uint32_t SPI_SPEED>
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public SAMHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> {};
#elif defined(AVR_HARDWARE_SPI)
template<uint32_t SPI_SPEED>
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public AVRHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> {};
#if defined(SPI_UART0_DATA)
template<uint32_t SPI_SPEED>
class SPIOutput<SPI_UART0_DATA, SPI_UART0_CLOCK, SPI_SPEED> : public AVRUSART0SPIOutput<SPI_UART0_DATA, SPI_UART0_CLOCK, SPI_SPEED> {};
#endif
#if defined(SPI_UART1_DATA)
template<uint32_t SPI_SPEED>
class SPIOutput<SPI_UART1_DATA, SPI_UART1_CLOCK, SPI_SPEED> : public AVRUSART1SPIOutput<SPI_UART1_DATA, SPI_UART1_CLOCK, SPI_SPEED> {};
#endif
#endif
#else
# if !defined(FASTLED_INTERNAL) && !defined(FASTLED_ALL_PINS_HARDWARE_SPI)
# ifdef FASTLED_HAS_PRAGMA_MESSAGE
# pragma message "No hardware SPI pins defined. All SPI access will default to bitbanged output"
# else
# warning "No hardware SPI pins defined. All SPI access will default to bitbanged output"
# endif
# endif
#endif
// #if defined(USART_DATA) && defined(USART_CLOCK)
// template<uint32_t SPI_SPEED>
// class AVRSPIOutput<USART_DATA, USART_CLOCK, SPI_SPEED> : public AVRUSARTSPIOutput<USART_DATA, USART_CLOCK, SPI_SPEED> {};
// #endif
#else
# if !defined(FASTLED_INTERNAL) && !defined(FASTLED_ALL_PINS_HARDWARE_SPI)
# ifdef FASTLED_HAS_PRAGMA_MESSAGE
# pragma message "Forcing software SPI - no hardware SPI for you!"
# else
# warning "Forcing software SPI - no hardware SPI for you!"
# endif
# endif
#endif
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,381 @@
#ifndef __INC_FASTSPI_BITBANG_H
#define __INC_FASTSPI_BITBANG_H
#include "FastLED.h"
#include "fastled_delay.h"
FASTLED_NAMESPACE_BEGIN
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Software SPI (aka bit-banging) support - with aggressive optimizations for when the clock and data pin are on the same port
//
// TODO: Replace the select pin definition with a set of pins, to allow using mux hardware for routing in the future
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint32_t SPI_SPEED>
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;
typedef typename FastPin<CLOCK_PIN>::port_ptr_t clock_ptr_t;
// The data type for what's at a pin's port - typedef'd here from the Pin definition because on avr the ports
// are 8 bits wide while on arm they are 32.
typedef typename FastPin<DATA_PIN>::port_t data_t;
typedef typename FastPin<CLOCK_PIN>::port_t clock_t;
Selectable *m_pSelect;
public:
AVRSoftwareSPIOutput() { m_pSelect = NULL; }
AVRSoftwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
void init() {
// set the pins to output and make sure the select is released (which apparently means hi? This is a bit
// confusing to me)
FastPin<DATA_PIN>::setOutput();
FastPin<CLOCK_PIN>::setOutput();
release();
}
// stop the SPI output. Pretty much a NOP with software, as there's no registers to kick
static void stop() { }
// 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) {
writeBit<7>(b);
writeBit<6>(b);
writeBit<5>(b);
writeBit<4>(b);
writeBit<3>(b);
writeBit<2>(b);
writeBit<1>(b);
writeBit<0>(b);
}
private:
// writeByte implementation with data/clock registers passed in.
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);
writeBit<4>(b, clockpin, datapin);
writeBit<3>(b, clockpin, datapin);
writeBit<2>(b, clockpin, datapin);
writeBit<1>(b, clockpin, datapin);
writeBit<0>(b, clockpin, datapin);
}
// 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,
// 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) {
writeBit<7>(b, datapin, hival, loval, hiclock, loclock);
writeBit<6>(b, datapin, hival, loval, hiclock, loclock);
writeBit<5>(b, datapin, hival, loval, hiclock, loclock);
writeBit<4>(b, datapin, hival, loval, hiclock, loclock);
writeBit<3>(b, datapin, hival, loval, hiclock, loclock);
writeBit<2>(b, datapin, hival, loval, hiclock, loclock);
writeBit<1>(b, datapin, hival, loval, hiclock, loclock);
writeBit<0>(b, datapin, hival, loval, hiclock, loclock);
}
// 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) {
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);
writeBit<4>(b, clockpin, datapin, hival, loval, hiclock, loclock);
writeBit<3>(b, clockpin, datapin, hival, loval, hiclock, loclock);
writeBit<2>(b, clockpin, datapin, hival, loval, hiclock, loclock);
writeBit<1>(b, clockpin, datapin, hival, loval, hiclock, loclock);
writeBit<0>(b, clockpin, datapin, hival, loval, hiclock, loclock);
}
public:
// We want to make sure that the clock pulse is held high for a nininum of 35ns.
#if defined(FASTLED_TEENSY4)
#define DELAY_NS (1000 / (SPI_SPEED/1000000))
#define CLOCK_HI_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0);
#define CLOCK_LO_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0);
#else
#define MIN_DELAY ((NS(35)>3) ? (NS(35) - 3) : 1)
#define CLOCK_HI_DELAY do { delaycycles<MIN_DELAY>(); delaycycles<((SPI_SPEED > 10) ? (((SPI_SPEED-6) / 2) - MIN_DELAY) : (SPI_SPEED))>(); } while(0);
#define CLOCK_LO_DELAY do { delaycycles<((SPI_SPEED > 10) ? ((SPI_SPEED-6) / 2) : (SPI_SPEED))>(); } while(0);
#endif
// 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) {
//cli();
if(b & (1 << BIT)) {
FastPin<DATA_PIN>::hi();
#ifdef ESP32
// try to ensure we never have adjacent write opcodes to the same register
FastPin<CLOCK_PIN>::lo();
FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY;
FastPin<CLOCK_PIN>::toggle(); CLOCK_LO_DELAY;
#else
FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY;
FastPin<CLOCK_PIN>::lo(); CLOCK_LO_DELAY;
#endif
} else {
FastPin<DATA_PIN>::lo();
FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY;
#ifdef ESP32
// try to ensure we never have adjacent write opcodes to the same register
FastPin<CLOCK_PIN>::toggle(); CLOCK_HI_DELAY;
#else
FastPin<CLOCK_PIN>::lo(); CLOCK_LO_DELAY;
#endif
}
//sei();
}
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)) {
FastPin<DATA_PIN>::hi(datapin);
FastPin<CLOCK_PIN>::hi(clockpin); CLOCK_HI_DELAY;
FastPin<CLOCK_PIN>::lo(clockpin); CLOCK_LO_DELAY;
} else {
FastPin<DATA_PIN>::lo(datapin);
FastPin<CLOCK_PIN>::hi(clockpin); CLOCK_HI_DELAY;
FastPin<CLOCK_PIN>::lo(clockpin); CLOCK_LO_DELAY;
}
}
// 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) {
// // only need to explicitly set clock hi if clock and data are on different ports
if(b & (1 << BIT)) {
FastPin<DATA_PIN>::fastset(datapin, hival);
FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); CLOCK_HI_DELAY;
FastPin<CLOCK_PIN>::fastset(clockpin, loclock); CLOCK_LO_DELAY;
} else {
// FL_NOP;
FastPin<DATA_PIN>::fastset(datapin, loval);
FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); CLOCK_HI_DELAY;
FastPin<CLOCK_PIN>::fastset(clockpin, loclock); CLOCK_LO_DELAY;
}
}
// 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) {
#if 0
writeBit<BIT>(b);
#else
if(b & (1 << BIT)) {
FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo);
FastPin<DATA_PIN>::fastset(clockdatapin, datahiclockhi); CLOCK_HI_DELAY;
FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); CLOCK_LO_DELAY;
} else {
// FL_NOP;
FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo);
FastPin<DATA_PIN>::fastset(clockdatapin, dataloclockhi); CLOCK_HI_DELAY;
FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); CLOCK_LO_DELAY;
}
#endif
}
public:
// select the SPI output (TODO: research whether this really means hi or lo. Alt TODO: move select responsibility out of the SPI classes
// entirely, make it up to the caller to remember to lock/select the line?)
void select() { if(m_pSelect != NULL) { m_pSelect->select(); } } // FastPin<SELECT_PIN>::hi(); }
// release the SPI line
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) {
select();
writeBytesValueRaw(value, len);
release();
}
static void writeBytesValueRaw(uint8_t value, int len) {
#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
// 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--) {
writeByte(value);
}
#else
register data_ptr_t datapin = FastPin<DATA_PIN>::port();
if(FastPin<DATA_PIN>::port() != FastPin<CLOCK_PIN>::port()) {
// If data and clock are on different ports, then writing a bit will consist of writing the value foor
// the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
register clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
register data_t datahi = FastPin<DATA_PIN>::hival();
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--) {
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
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--) {
writeByte(value, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
}
}
#endif
}
// 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) {
select();
#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
uint8_t *end = data + len;
while(data != end) {
writeByte(D::adjust(*data++));
}
#else
register clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
register data_ptr_t datapin = FastPin<DATA_PIN>::port();
if(FastPin<DATA_PIN>::port() != FastPin<CLOCK_PIN>::port()) {
// If data and clock are on different ports, then writing a bit will consist of writing the value foor
// the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
register data_t datahi = FastPin<DATA_PIN>::hival();
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) {
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
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) {
writeByte(D::adjust(*data++), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
}
// FastPin<CLOCK_PIN>::lo();
}
#endif
D::postBlock(len);
release();
}
// default version of writing a block of data out to the SPI port, with no data modifications being made
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 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 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
while(pixels.has(1)) {
if(FLAGS & FLAG_START_BIT) {
writeBit<0>(1);
}
writeByte(D::adjust(pixels.loadAndScale0()));
writeByte(D::adjust(pixels.loadAndScale1()));
writeByte(D::adjust(pixels.loadAndScale2()));
pixels.advanceData();
pixels.stepDithering();
}
#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)
// then we can use a bunch of optimizations in here
register data_ptr_t datapin = FastPin<DATA_PIN>::port();
if(FastPin<DATA_PIN>::port() != FastPin<CLOCK_PIN>::port()) {
register clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
// If data and clock are on different ports, then writing a bit will consist of writing the value foor
// the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
register data_t datahi = FastPin<DATA_PIN>::hival();
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(pixels.has(1)) {
if(FLAGS & FLAG_START_BIT) {
writeBit<0>(1, clockpin, datapin, datahi, datalo, clockhi, clocklo);
}
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
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(pixels.has(1)) {
if(FLAGS & FLAG_START_BIT) {
writeBit<0>(1, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
}
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();
}
};
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,64 @@
#ifndef __INC_FASTSPI_NOP_H
#define __INC_FASTSPI_NOP_H
#if 0 // Guard against the arduino ide idiotically including every header file
#include "FastLED.h"
FASTLED_NAMESPACE_BEGIN
/// A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should
/// be used as a definition for the set of methods that the spi implementation classes should use (since C++ doesn't support the
/// idea of interfaces - it's possible this could be done with virtual classes, need to decide if i want that overhead)
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class NOPSPIOutput {
Selectable *m_pSelect;
public:
NOPSPIOutput() { m_pSelect = NULL; }
NOPSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
/// set the object representing the selectable
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
/// 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();
/// not the most efficient mechanism in the world - but should be enough for sm16716 and friends
template <uint8_t BIT> inline static void writeBit(uint8_t b) { /* TODO */ }
/// write a byte out via SPI (returns immediately on writing register)
void writeByte(uint8_t b) { /* TODO */ }
/// write a word out via SPI (returns immediately on writing register)
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 */ }
/// write out pixel data from the given PixelController object
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) { /* TODO */ }
};
FASTLED_NAMESPACE_END
#endif
#endif

View File

@@ -0,0 +1,96 @@
#ifndef __INC_FASTSPI_ARM_SAM_H
#define __INC_FASTSPI_ARM_SAM_H
#if 0 // guard against the arduino ide idiotically including every header file
#include "FastLED.h"
FASTLED_NAMESPACE_BEGIN
// A skeletal implementation of hardware SPI support. Fill in the necessary code for init, waiting, and writing. The rest of
// the method implementations should provide a starting point, even if not hte most efficient to start with
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_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 writePixels(PixelController<RGB_ORDER> pixels) {
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();
}
};
FASTLED_NAMESPACE_END
#endif
#endif

View File

@@ -0,0 +1,43 @@
#ifndef __INC_FASTSPI_TYPES_H
#define __INC_FASTSPI_TYPES_H
#include "FastLED.h"
FASTLED_NAMESPACE_BEGIN
// Some helper macros for getting at mis-ordered byte values
#define SPI_B0 (RGB_BYTE0(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
#define SPI_B1 (RGB_BYTE1(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
#define SPI_B2 (RGB_BYTE2(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
#define SPI_ADVANCE (3 + (MASK_SKIP_BITS & SKIP))
/// Some of the SPI controllers will need to perform a transform on each byte before doing
/// anyting with it. Creating a class of this form and passing it in as a template parameter to
/// writeBytes/writeBytes3 below will ensure that the body of this method will get called on every
/// byte worked on. Recommendation, make the adjust method aggressively inlined.
///
/// TODO: Convinience macro for building these
class DATA_NOP {
public:
static __attribute__((always_inline)) inline uint8_t adjust(register uint8_t data) { return data; }
static __attribute__((always_inline)) inline uint8_t adjust(register uint8_t data, register uint8_t scale) { return scale8(data, scale); }
static __attribute__((always_inline)) inline void postBlock(int /* len */) { }
};
#define FLAG_START_BIT 0x80
#define MASK_SKIP_BITS 0x3F
// Clock speed dividers
#define SPEED_DIV_2 2
#define SPEED_DIV_4 4
#define SPEED_DIV_8 8
#define SPEED_DIV_16 16
#define SPEED_DIV_32 32
#define SPEED_DIV_64 64
#define SPEED_DIV_128 128
#define MAX_DATA_RATE 0
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,714 @@
#define FASTLED_INTERNAL
#include <stdint.h>
#include "FastLED.h"
FASTLED_NAMESPACE_BEGIN
// Functions to convert HSV colors to RGB colors.
//
// The basically fall into two groups: spectra, and rainbows.
// Spectra and rainbows are not the same thing. Wikipedia has a good
// illustration here
// http://upload.wikimedia.org/wikipedia/commons/f/f6/Prism_compare_rainbow_01.png
// from this article
// http://en.wikipedia.org/wiki/Rainbow#Number_of_colours_in_spectrum_or_rainbow
// that shows a 'spectrum' and a 'rainbow' side by side. Among other
// differences, you'll see that a 'rainbow' has much more yellow than
// a plain spectrum. "Classic" LED color washes are spectrum based, and
// usually show very little yellow.
//
// Wikipedia's page on HSV color space, with pseudocode for conversion
// to RGB color space
// http://en.wikipedia.org/wiki/HSL_and_HSV
// Note that their conversion algorithm, which is (naturally) very popular
// is in the "maximum brightness at any given hue" style, vs the "uniform
// brightness for all hues" style.
//
// You can't have both; either purple is the same brightness as red, e.g
// red = #FF0000 and purple = #800080 -> same "total light" output
// OR purple is 'as bright as it can be', e.g.
// red = #FF0000 and purple = #FF00FF -> purple is much brighter than red.
// The colorspace conversions here try to keep the apparent brightness
// constant even as the hue varies.
//
// Adafruit's "Wheel" function, discussed here
// http://forums.adafruit.com/viewtopic.php?f=47&t=22483
// is also of the "constant apparent brightness" variety.
//
// TODO: provide the 'maximum brightness no matter what' variation.
//
// See also some good, clear Arduino C code from Kasper Kamperman
// http://www.kasperkamperman.com/blog/arduino/arduino-programming-hsb-to-rgb/
// which in turn was was based on Windows C code from "nico80"
// http://www.codeproject.com/Articles/9207/An-HSB-RGBA-colour-picker
void hsv2rgb_raw_C (const struct CHSV & hsv, struct CRGB & rgb);
void hsv2rgb_raw_avr(const struct CHSV & hsv, struct CRGB & rgb);
#if defined(__AVR__) && !defined( LIB8_ATTINY )
void hsv2rgb_raw(const struct CHSV & hsv, struct CRGB & rgb)
{
hsv2rgb_raw_avr( hsv, rgb);
}
#else
void hsv2rgb_raw(const struct CHSV & hsv, struct CRGB & rgb)
{
hsv2rgb_raw_C( hsv, rgb);
}
#endif
#define APPLY_DIMMING(X) (X)
#define HSV_SECTION_6 (0x20)
#define HSV_SECTION_3 (0x40)
void hsv2rgb_raw_C (const struct CHSV & hsv, struct CRGB & rgb)
{
// Convert hue, saturation and brightness ( HSV/HSB ) to RGB
// "Dimming" is used on saturation and brightness to make
// the output more visually linear.
// Apply dimming curves
uint8_t value = APPLY_DIMMING( hsv.val);
uint8_t saturation = hsv.sat;
// The brightness floor is minimum number that all of
// R, G, and B will be set to.
uint8_t invsat = APPLY_DIMMING( 255 - saturation);
uint8_t brightness_floor = (value * invsat) / 256;
// The color amplitude is the maximum amount of R, G, and B
// that will be added on top of the brightness_floor to
// create the specific hue desired.
uint8_t color_amplitude = value - brightness_floor;
// Figure out which section of the hue wheel we're in,
// and how far offset we are withing that section
uint8_t section = hsv.hue / HSV_SECTION_3; // 0..2
uint8_t offset = hsv.hue % HSV_SECTION_3; // 0..63
uint8_t rampup = offset; // 0..63
uint8_t rampdown = (HSV_SECTION_3 - 1) - offset; // 63..0
// We now scale rampup and rampdown to a 0-255 range -- at least
// in theory, but here's where architecture-specific decsions
// come in to play:
// To scale them up to 0-255, we'd want to multiply by 4.
// But in the very next step, we multiply the ramps by other
// values and then divide the resulting product by 256.
// So which is faster?
// ((ramp * 4) * othervalue) / 256
// or
// ((ramp ) * othervalue) / 64
// It depends on your processor architecture.
// On 8-bit AVR, the "/ 256" is just a one-cycle register move,
// but the "/ 64" might be a multicycle shift process. So on AVR
// it's faster do multiply the ramp values by four, and then
// divide by 256.
// On ARM, the "/ 256" and "/ 64" are one cycle each, so it's
// faster to NOT multiply the ramp values by four, and just to
// divide the resulting product by 64 (instead of 256).
// Moral of the story: trust your profiler, not your insticts.
// Since there's an AVR assembly version elsewhere, we'll
// assume what we're on an architecture where any number of
// bit shifts has roughly the same cost, and we'll remove the
// redundant math at the source level:
// // scale up to 255 range
// //rampup *= 4; // 0..252
// //rampdown *= 4; // 0..252
// compute color-amplitude-scaled-down versions of rampup and rampdown
uint8_t rampup_amp_adj = (rampup * color_amplitude) / (256 / 4);
uint8_t rampdown_amp_adj = (rampdown * color_amplitude) / (256 / 4);
// add brightness_floor offset to everything
uint8_t rampup_adj_with_floor = rampup_amp_adj + brightness_floor;
uint8_t rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor;
if( section ) {
if( section == 1) {
// section 1: 0x40..0x7F
rgb.r = brightness_floor;
rgb.g = rampdown_adj_with_floor;
rgb.b = rampup_adj_with_floor;
} else {
// section 2; 0x80..0xBF
rgb.r = rampup_adj_with_floor;
rgb.g = brightness_floor;
rgb.b = rampdown_adj_with_floor;
}
} else {
// section 0: 0x00..0x3F
rgb.r = rampdown_adj_with_floor;
rgb.g = rampup_adj_with_floor;
rgb.b = brightness_floor;
}
}
#if defined(__AVR__) && !defined( LIB8_ATTINY )
void hsv2rgb_raw_avr(const struct CHSV & hsv, struct CRGB & rgb)
{
uint8_t hue, saturation, value;
hue = hsv.hue;
saturation = hsv.sat;
value = hsv.val;
// Saturation more useful the other way around
saturation = 255 - saturation;
uint8_t invsat = APPLY_DIMMING( saturation );
// Apply dimming curves
value = APPLY_DIMMING( value );
// The brightness floor is minimum number that all of
// R, G, and B will be set to, which is value * invsat
uint8_t brightness_floor;
asm volatile(
"mul %[value], %[invsat] \n"
"mov %[brightness_floor], r1 \n"
: [brightness_floor] "=r" (brightness_floor)
: [value] "r" (value),
[invsat] "r" (invsat)
: "r0", "r1"
);
// The color amplitude is the maximum amount of R, G, and B
// that will be added on top of the brightness_floor to
// create the specific hue desired.
uint8_t color_amplitude = value - brightness_floor;
// Figure how far we are offset into the section of the
// color wheel that we're in
uint8_t offset = hsv.hue & (HSV_SECTION_3 - 1); // 0..63
uint8_t rampup = offset * 4; // 0..252
// compute color-amplitude-scaled-down versions of rampup and rampdown
uint8_t rampup_amp_adj;
uint8_t rampdown_amp_adj;
asm volatile(
"mul %[rampup], %[color_amplitude] \n"
"mov %[rampup_amp_adj], r1 \n"
"com %[rampup] \n"
"mul %[rampup], %[color_amplitude] \n"
"mov %[rampdown_amp_adj], r1 \n"
: [rampup_amp_adj] "=&r" (rampup_amp_adj),
[rampdown_amp_adj] "=&r" (rampdown_amp_adj),
[rampup] "+r" (rampup)
: [color_amplitude] "r" (color_amplitude)
: "r0", "r1"
);
// add brightness_floor offset to everything
uint8_t rampup_adj_with_floor = rampup_amp_adj + brightness_floor;
uint8_t rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor;
// keep gcc from using "X" as the index register for storing
// results back in the return structure. AVR's X register can't
// do "std X+q, rnn", but the Y and Z registers can.
// if the pointer to 'rgb' is in X, gcc will add all kinds of crazy
// extra instructions. Simply killing X here seems to help it
// try Y or Z first.
asm volatile( "" : : : "r26", "r27" );
if( hue & 0x80 ) {
// section 2: 0x80..0xBF
rgb.r = rampup_adj_with_floor;
rgb.g = brightness_floor;
rgb.b = rampdown_adj_with_floor;
} else {
if( hue & 0x40) {
// section 1: 0x40..0x7F
rgb.r = brightness_floor;
rgb.g = rampdown_adj_with_floor;
rgb.b = rampup_adj_with_floor;
} else {
// section 0: 0x00..0x3F
rgb.r = rampdown_adj_with_floor;
rgb.g = rampup_adj_with_floor;
rgb.b = brightness_floor;
}
}
cleanup_R1();
}
// End of AVR asm implementation
#endif
void hsv2rgb_spectrum( const CHSV& hsv, CRGB& rgb)
{
CHSV hsv2(hsv);
hsv2.hue = scale8( hsv2.hue, 191);
hsv2rgb_raw(hsv2, rgb);
}
// Sometimes the compiler will do clever things to reduce
// code size that result in a net slowdown, if it thinks that
// a variable is not used in a certain location.
// This macro does its best to convince the compiler that
// the variable is used in this location, to help control
// code motion and de-duplication that would result in a slowdown.
#define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) )
#define K255 255
#define K171 171
#define K170 170
#define K85 85
void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb)
{
// Yellow has a higher inherent brightness than
// any other color; 'pure' yellow is perceived to
// be 93% as bright as white. In order to make
// yellow appear the correct relative brightness,
// it has to be rendered brighter than all other
// colors.
// Level Y1 is a moderate boost, the default.
// Level Y2 is a strong boost.
const uint8_t Y1 = 1;
const uint8_t Y2 = 0;
// G2: Whether to divide all greens by two.
// Depends GREATLY on your particular LEDs
const uint8_t G2 = 0;
// Gscale: what to scale green down by.
// Depends GREATLY on your particular LEDs
const uint8_t Gscale = 0;
uint8_t hue = hsv.hue;
uint8_t sat = hsv.sat;
uint8_t val = hsv.val;
uint8_t offset = hue & 0x1F; // 0..31
// offset8 = offset * 8
uint8_t offset8 = offset;
{
#if defined(__AVR__)
// Left to its own devices, gcc turns "x <<= 3" into a loop
// It's much faster and smaller to just do three single-bit shifts
// So this business is to force that.
offset8 <<= 1;
asm volatile("");
offset8 <<= 1;
asm volatile("");
offset8 <<= 1;
#else
// On ARM and other non-AVR platforms, we just shift 3.
offset8 <<= 3;
#endif
}
uint8_t third = scale8( offset8, (256 / 3)); // max = 85
uint8_t r, g, b;
if( ! (hue & 0x80) ) {
// 0XX
if( ! (hue & 0x40) ) {
// 00X
//section 0-1
if( ! (hue & 0x20) ) {
// 000
//case 0: // R -> O
r = K255 - third;
g = third;
b = 0;
FORCE_REFERENCE(b);
} else {
// 001
//case 1: // O -> Y
if( Y1 ) {
r = K171;
g = K85 + third ;
b = 0;
FORCE_REFERENCE(b);
}
if( Y2 ) {
r = K170 + third;
//uint8_t twothirds = (third << 1);
uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170
g = K85 + twothirds;
b = 0;
FORCE_REFERENCE(b);
}
}
} else {
//01X
// section 2-3
if( ! (hue & 0x20) ) {
// 010
//case 2: // Y -> G
if( Y1 ) {
//uint8_t twothirds = (third << 1);
uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170
r = K171 - twothirds;
g = K170 + third;
b = 0;
FORCE_REFERENCE(b);
}
if( Y2 ) {
r = K255 - offset8;
g = K255;
b = 0;
FORCE_REFERENCE(b);
}
} else {
// 011
// case 3: // G -> A
r = 0;
FORCE_REFERENCE(r);
g = K255 - third;
b = third;
}
}
} else {
// section 4-7
// 1XX
if( ! (hue & 0x40) ) {
// 10X
if( ! ( hue & 0x20) ) {
// 100
//case 4: // A -> B
r = 0;
FORCE_REFERENCE(r);
//uint8_t twothirds = (third << 1);
uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170
g = K171 - twothirds; //K170?
b = K85 + twothirds;
} else {
// 101
//case 5: // B -> P
r = third;
g = 0;
FORCE_REFERENCE(g);
b = K255 - third;
}
} else {
if( ! (hue & 0x20) ) {
// 110
//case 6: // P -- K
r = K85 + third;
g = 0;
FORCE_REFERENCE(g);
b = K171 - third;
} else {
// 111
//case 7: // K -> R
r = K170 + third;
g = 0;
FORCE_REFERENCE(g);
b = K85 - third;
}
}
}
// This is one of the good places to scale the green down,
// although the client can scale green down as well.
if( G2 ) g = g >> 1;
if( Gscale ) g = scale8_video_LEAVING_R1_DIRTY( g, Gscale);
// Scale down colors if we're desaturated at all
// and add the brightness_floor to r, g, and b.
if( sat != 255 ) {
if( sat == 0) {
r = 255; b = 255; g = 255;
} else {
//nscale8x3_video( r, g, b, sat);
#if (FASTLED_SCALE8_FIXED==1)
if( r ) r = scale8_LEAVING_R1_DIRTY( r, sat);
if( g ) g = scale8_LEAVING_R1_DIRTY( g, sat);
if( b ) b = scale8_LEAVING_R1_DIRTY( b, sat);
#else
if( r ) r = scale8_LEAVING_R1_DIRTY( r, sat) + 1;
if( g ) g = scale8_LEAVING_R1_DIRTY( g, sat) + 1;
if( b ) b = scale8_LEAVING_R1_DIRTY( b, sat) + 1;
#endif
cleanup_R1();
uint8_t desat = 255 - sat;
desat = scale8( desat, desat);
uint8_t brightness_floor = desat;
r += brightness_floor;
g += brightness_floor;
b += brightness_floor;
}
}
// Now scale everything down if we're at value < 255.
if( val != 255 ) {
val = scale8_video_LEAVING_R1_DIRTY( val, val);
if( val == 0 ) {
r=0; g=0; b=0;
} else {
// nscale8x3_video( r, g, b, val);
#if (FASTLED_SCALE8_FIXED==1)
if( r ) r = scale8_LEAVING_R1_DIRTY( r, val);
if( g ) g = scale8_LEAVING_R1_DIRTY( g, val);
if( b ) b = scale8_LEAVING_R1_DIRTY( b, val);
#else
if( r ) r = scale8_LEAVING_R1_DIRTY( r, val) + 1;
if( g ) g = scale8_LEAVING_R1_DIRTY( g, val) + 1;
if( b ) b = scale8_LEAVING_R1_DIRTY( b, val) + 1;
#endif
cleanup_R1();
}
}
// Here we have the old AVR "missing std X+n" problem again
// It turns out that fixing it winds up costing more than
// not fixing it.
// To paraphrase Dr Bronner, profile! profile! profile!
//asm volatile( "" : : : "r26", "r27" );
//asm volatile (" movw r30, r26 \n" : : : "r30", "r31");
rgb.r = r;
rgb.g = g;
rgb.b = b;
}
void hsv2rgb_raw(const struct CHSV * phsv, struct CRGB * prgb, int numLeds) {
for(int i = 0; i < numLeds; ++i) {
hsv2rgb_raw(phsv[i], prgb[i]);
}
}
void hsv2rgb_rainbow( const struct CHSV* phsv, struct CRGB * prgb, int numLeds) {
for(int i = 0; i < numLeds; ++i) {
hsv2rgb_rainbow(phsv[i], prgb[i]);
}
}
void hsv2rgb_spectrum( const struct CHSV* phsv, struct CRGB * prgb, int numLeds) {
for(int i = 0; i < numLeds; ++i) {
hsv2rgb_spectrum(phsv[i], prgb[i]);
}
}
#define FIXFRAC8(N,D) (((N)*256)/(D))
// This function is only an approximation, and it is not
// nearly as fast as the normal HSV-to-RGB conversion.
// See extended notes in the .h file.
CHSV rgb2hsv_approximate( const CRGB& rgb)
{
uint8_t r = rgb.r;
uint8_t g = rgb.g;
uint8_t b = rgb.b;
uint8_t h, s, v;
// find desaturation
uint8_t desat = 255;
if( r < desat) desat = r;
if( g < desat) desat = g;
if( b < desat) desat = b;
// remove saturation from all channels
r -= desat;
g -= desat;
b -= desat;
//Serial.print("desat="); Serial.print(desat); Serial.println("");
//uint8_t orig_desat = sqrt16( desat * 256);
//Serial.print("orig_desat="); Serial.print(orig_desat); Serial.println("");
// saturation is opposite of desaturation
s = 255 - desat;
//Serial.print("s.1="); Serial.print(s); Serial.println("");
if( s != 255 ) {
// undo 'dimming' of saturation
s = 255 - sqrt16( (255-s) * 256);
}
// without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5
// if( s != 255 ) s = (255 - (256.0 * sqrt( (float)(255-s) / 256.0)));
//Serial.print("s.2="); Serial.print(s); Serial.println("");
// at least one channel is now zero
// if all three channels are zero, we had a
// shade of gray.
if( (r + g + b) == 0) {
// we pick hue zero for no special reason
return CHSV( 0, 0, 255 - s);
}
// scale all channels up to compensate for desaturation
if( s < 255) {
if( s == 0) s = 1;
uint32_t scaleup = 65535 / (s);
r = ((uint32_t)(r) * scaleup) / 256;
g = ((uint32_t)(g) * scaleup) / 256;
b = ((uint32_t)(b) * scaleup) / 256;
}
//Serial.print("r.2="); Serial.print(r); Serial.println("");
//Serial.print("g.2="); Serial.print(g); Serial.println("");
//Serial.print("b.2="); Serial.print(b); Serial.println("");
uint16_t total = r + g + b;
//Serial.print("total="); Serial.print(total); Serial.println("");
// scale all channels up to compensate for low values
if( total < 255) {
if( total == 0) total = 1;
uint32_t scaleup = 65535 / (total);
r = ((uint32_t)(r) * scaleup) / 256;
g = ((uint32_t)(g) * scaleup) / 256;
b = ((uint32_t)(b) * scaleup) / 256;
}
//Serial.print("r.3="); Serial.print(r); Serial.println("");
//Serial.print("g.3="); Serial.print(g); Serial.println("");
//Serial.print("b.3="); Serial.print(b); Serial.println("");
if( total > 255 ) {
v = 255;
} else {
v = qadd8(desat,total);
// undo 'dimming' of brightness
if( v != 255) v = sqrt16( v * 256);
// without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5
// if( v != 255) v = (256.0 * sqrt( (float)(v) / 256.0));
}
//Serial.print("v="); Serial.print(v); Serial.println("");
#if 0
//#else
if( v != 255) {
// this part could probably use refinement/rethinking,
// (but it doesn't overflow & wrap anymore)
uint16_t s16;
s16 = (s * 256);
s16 /= v;
//Serial.print("s16="); Serial.print(s16); Serial.println("");
if( s16 < 256) {
s = s16;
} else {
s = 255; // clamp to prevent overflow
}
}
#endif
//Serial.print("s.3="); Serial.print(s); Serial.println("");
// since this wasn't a pure shade of gray,
// the interesting question is what hue is it
// start with which channel is highest
// (ties don't matter)
uint8_t highest = r;
if( g > highest) highest = g;
if( b > highest) highest = b;
if( highest == r ) {
// Red is highest.
// Hue could be Purple/Pink-Red,Red-Orange,Orange-Yellow
if( g == 0 ) {
// if green is zero, we're in Purple/Pink-Red
h = (HUE_PURPLE + HUE_PINK) / 2;
h += scale8( qsub8(r, 128), FIXFRAC8(48,128));
} else if ( (r - g) > g) {
// if R-G > G then we're in Red-Orange
h = HUE_RED;
h += scale8( g, FIXFRAC8(32,85));
} else {
// R-G < G, we're in Orange-Yellow
h = HUE_ORANGE;
h += scale8( qsub8((g - 85) + (171 - r), 4), FIXFRAC8(32,85)); //221
}
} else if ( highest == g) {
// Green is highest
// Hue could be Yellow-Green, Green-Aqua
if( b == 0) {
// if Blue is zero, we're in Yellow-Green
// G = 171..255
// R = 171.. 0
h = HUE_YELLOW;
uint8_t radj = scale8( qsub8(171,r), 47); //171..0 -> 0..171 -> 0..31
uint8_t gadj = scale8( qsub8(g,171), 96); //171..255 -> 0..84 -> 0..31;
uint8_t rgadj = radj + gadj;
uint8_t hueadv = rgadj / 2;
h += hueadv;
//h += scale8( qadd8( 4, qadd8((g - 128), (128 - r))),
// FIXFRAC8(32,255)); //
} else {
// if Blue is nonzero we're in Green-Aqua
if( (g-b) > b) {
h = HUE_GREEN;
h += scale8( b, FIXFRAC8(32,85));
} else {
h = HUE_AQUA;
h += scale8( qsub8(b, 85), FIXFRAC8(8,42));
}
}
} else /* highest == b */ {
// Blue is highest
// Hue could be Aqua/Blue-Blue, Blue-Purple, Purple-Pink
if( r == 0) {
// if red is zero, we're in Aqua/Blue-Blue
h = HUE_AQUA + ((HUE_BLUE - HUE_AQUA) / 4);
h += scale8( qsub8(b, 128), FIXFRAC8(24,128));
} else if ( (b-r) > r) {
// B-R > R, we're in Blue-Purple
h = HUE_BLUE;
h += scale8( r, FIXFRAC8(32,85));
} else {
// B-R < R, we're in Purple-Pink
h = HUE_PURPLE;
h += scale8( qsub8(r, 85), FIXFRAC8(32,85));
}
}
h += 1;
return CHSV( h, s, v);
}
// Examples that need work:
// 0,192,192
// 192,64,64
// 224,32,32
// 252,0,126
// 252,252,0
// 252,252,126
FASTLED_NAMESPACE_END

View File

@@ -0,0 +1,91 @@
#ifndef __INC_HSV2RGB_H
#define __INC_HSV2RGB_H
#include "FastLED.h"
#include "pixeltypes.h"
FASTLED_NAMESPACE_BEGIN
// hsv2rgb_rainbow - convert a hue, saturation, and value to RGB
// using a visually balanced rainbow (vs a straight
// mathematical spectrum).
// This 'rainbow' yields better yellow and orange
// than a straight 'spectrum'.
//
// NOTE: here hue is 0-255, not just 0-191
void hsv2rgb_rainbow( const struct CHSV& hsv, struct CRGB& rgb);
void hsv2rgb_rainbow( const struct CHSV* phsv, struct CRGB * prgb, int numLeds);
#define HUE_MAX_RAINBOW 255
// hsv2rgb_spectrum - convert a hue, saturation, and value to RGB
// using a mathematically straight spectrum (vs
// a visually balanced rainbow).
// This 'spectrum' will have more green & blue
// than a 'rainbow', and less yellow and orange.
//
// NOTE: here hue is 0-255, not just 0-191
void hsv2rgb_spectrum( const struct CHSV& hsv, struct CRGB& rgb);
void hsv2rgb_spectrum( const struct CHSV* phsv, struct CRGB * prgb, int numLeds);
#define HUE_MAX_SPECTRUM 255
// hsv2rgb_raw - convert hue, saturation, and value to RGB.
// This 'spectrum' conversion will be more green & blue
// than a real 'rainbow', and the hue is specified just
// in the range 0-191. Together, these result in a
// slightly faster conversion speed, at the expense of
// color balance.
//
// NOTE: Hue is 0-191 only!
// Saturation & value are 0-255 each.
//
void hsv2rgb_raw(const struct CHSV& hsv, struct CRGB & rgb);
void hsv2rgb_raw(const struct CHSV* phsv, struct CRGB * prgb, int numLeds);
#define HUE_MAX 191
// rgb2hsv_approximate - recover _approximate_ HSV values from RGB.
//
// NOTE 1: This function is a long-term work in process; expect
// results to change slightly over time as this function is
// refined and improved.
//
// NOTE 2: This function is most accurate when the input is an
// RGB color that came from a fully-saturated HSV color to start
// with. E.g. CHSV( hue, 255, 255) -> CRGB -> CHSV will give
// best results.
//
// NOTE 3: This function is not nearly as fast as HSV-to-RGB.
// It is provided for those situations when the need for this
// function cannot be avoided, or when extremely high performance
// is not needed.
//
// NOTE 4: Why is this 'only' an "approximation"?
// Not all RGB colors have HSV equivalents! For example, there
// is no HSV value that will ever convert to RGB(255,255,0) using
// the code provided in this library. So if you try to
// convert RGB(255,255,0) 'back' to HSV, you'll necessarily get
// only an approximation. Emphasis has been placed on getting
// the 'hue' as close as usefully possible, but even that's a bit
// of a challenge. The 8-bit HSV and 8-bit RGB color spaces
// are not a "bijection".
//
// Nevertheless, this function does a pretty good job, particularly
// at recovering the 'hue' from fully saturated RGB colors that
// originally came from HSV rainbow colors. So if you start
// with CHSV(hue_in,255,255), and convert that to RGB, and then
// convert it back to HSV using this function, the resulting output
// hue will either exactly the same, or very close (+/-1).
// The more desaturated the original RGB color is, the rougher the
// approximation, and the less accurate the results.
//
CHSV rgb2hsv_approximate( const CRGB& rgb);
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,69 @@
#ifndef __INC_LED_SYSDEFS_H
#define __INC_LED_SYSDEFS_H
#include "FastLED.h"
#include "fastled_config.h"
#if defined(NRF51) || defined(__RFduino__) || defined (__Simblee__)
#include "platforms/arm/nrf51/led_sysdefs_arm_nrf51.h"
#elif defined(NRF52_SERIES)
#include "platforms/arm/nrf52/led_sysdefs_arm_nrf52.h"
#elif defined(__MK20DX128__) || defined(__MK20DX256__)
// Include k20/T3 headers
#include "platforms/arm/k20/led_sysdefs_arm_k20.h"
#elif defined(__MK66FX1M0__) || defined(__MK64FX512__)
// Include k66/T3.6 headers
#include "platforms/arm/k66/led_sysdefs_arm_k66.h"
#elif defined(__MKL26Z64__)
// Include kl26/T-LC headers
#include "platforms/arm/kl26/led_sysdefs_arm_kl26.h"
#elif defined(__IMXRT1062__)
// teensy4
#include "platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h"
#elif defined(__SAM3X8E__)
// Include sam/due headers
#include "platforms/arm/sam/led_sysdefs_arm_sam.h"
#elif defined(STM32F10X_MD) || defined(__STM32F1__)
#include "platforms/arm/stm32/led_sysdefs_arm_stm32.h"
#elif defined(__SAMD21G18A__) || defined(__SAMD21J18A__) || defined(__SAMD21E17A__) || defined(__SAMD21E18A__) || defined(__SAMD51G19A__) || defined(__SAMD51J19A__)
#include "platforms/arm/d21/led_sysdefs_arm_d21.h"
#elif defined(ESP8266)
#include "platforms/esp/8266/led_sysdefs_esp8266.h"
#elif defined(ESP32)
#include "platforms/esp/32/led_sysdefs_esp32.h"
#elif defined(__AVR__) || defined(__AVR_ATmega4809__)
// AVR platforms
#include "platforms/avr/led_sysdefs_avr.h"
#elif defined(ARDUINO_ARCH_APOLLO3)
// Apollo3 platforms (e.g. the Ambiq Micro Apollo3 Blue as used by the SparkFun Artemis platforms)
#include "platforms/apollo3/led_sysdefs_apollo3.h"
#else
//
// We got here because we don't recognize the platform that you're
// trying to compile for: it's not AVR, or an ESP or ARM that we recognize.
//
// If you're reading this because you got the error below,
// and if this new platform is just a minor variant of an
// existing supported ARM platform, you may be able to add
// a new 'defined(XXX)' selector in the apporpriate code above.
//
// If this platform is a new microcontroller, see "PORTING.md".
//
#error "This platform isn't recognized by FastLED... yet. See comments in FastLED/led_sysdefs.h for options."
#endif
#ifndef FASTLED_NAMESPACE_BEGIN
#define FASTLED_NAMESPACE_BEGIN
#define FASTLED_NAMESPACE_END
#define FASTLED_USING_NAMESPACE
#endif
// Arduino.h needed for convenience functions digitalPinToPort/BitMask/portOutputRegister and the pinMode methods.
#ifdef ARDUINO
#include <Arduino.h>
#endif
#define CLKS_PER_US (F_CPU/1000000)
#endif

View File

@@ -0,0 +1,251 @@
#define FASTLED_INTERNAL
#include <stdint.h>
#include "FastLED.h"
FASTLED_NAMESPACE_BEGIN
#define RAND16_SEED 1337
uint16_t rand16seed = RAND16_SEED;
// memset8, memcpy8, memmove8:
// optimized avr replacements for the standard "C" library
// routines memset, memcpy, and memmove.
//
// There are two techniques that make these routines
// faster than the standard avr-libc routines.
// First, the loops are unrolled 2X, meaning that
// the average loop overhead is cut in half.
// And second, the compare-and-branch at the bottom
// of each loop decrements the low byte of the
// counter, and if the carry is clear, it branches
// back up immediately. Only if the low byte math
// causes carry do we bother to decrement the high
// byte and check that result for carry as well.
// Results for a 100-byte buffer are 20-40% faster
// than standard avr-libc, at a cost of a few extra
// bytes of code.
#if defined(__AVR__)
extern "C" {
//__attribute__ ((noinline))
void * memset8 ( void * ptr, uint8_t val, uint16_t num )
{
asm volatile(
" movw r26, %[ptr] \n\t"
" sbrs %A[num], 0 \n\t"
" rjmp Lseteven_%= \n\t"
" rjmp Lsetodd_%= \n\t"
"Lsetloop_%=: \n\t"
" st X+, %[val] \n\t"
"Lsetodd_%=: \n\t"
" st X+, %[val] \n\t"
"Lseteven_%=: \n\t"
" subi %A[num], 2 \n\t"
" brcc Lsetloop_%= \n\t"
" sbci %B[num], 0 \n\t"
" brcc Lsetloop_%= \n\t"
: [num] "+r" (num)
: [ptr] "r" (ptr),
[val] "r" (val)
: "memory"
);
return ptr;
}
//__attribute__ ((noinline))
void * memcpy8 ( void * dst, const void* src, uint16_t num )
{
asm volatile(
" movw r30, %[src] \n\t"
" movw r26, %[dst] \n\t"
" sbrs %A[num], 0 \n\t"
" rjmp Lcpyeven_%= \n\t"
" rjmp Lcpyodd_%= \n\t"
"Lcpyloop_%=: \n\t"
" ld __tmp_reg__, Z+ \n\t"
" st X+, __tmp_reg__ \n\t"
"Lcpyodd_%=: \n\t"
" ld __tmp_reg__, Z+ \n\t"
" st X+, __tmp_reg__ \n\t"
"Lcpyeven_%=: \n\t"
" subi %A[num], 2 \n\t"
" brcc Lcpyloop_%= \n\t"
" sbci %B[num], 0 \n\t"
" brcc Lcpyloop_%= \n\t"
: [num] "+r" (num)
: [src] "r" (src),
[dst] "r" (dst)
: "memory"
);
return dst;
}
//__attribute__ ((noinline))
void * memmove8 ( void * dst, const void* src, uint16_t num )
{
if( src > dst) {
// if src > dst then we can use the forward-stepping memcpy8
return memcpy8( dst, src, num);
} else {
// if src < dst then we have to step backward:
dst = (char*)dst + num;
src = (char*)src + num;
asm volatile(
" movw r30, %[src] \n\t"
" movw r26, %[dst] \n\t"
" sbrs %A[num], 0 \n\t"
" rjmp Lmoveven_%= \n\t"
" rjmp Lmovodd_%= \n\t"
"Lmovloop_%=: \n\t"
" ld __tmp_reg__, -Z \n\t"
" st -X, __tmp_reg__ \n\t"
"Lmovodd_%=: \n\t"
" ld __tmp_reg__, -Z \n\t"
" st -X, __tmp_reg__ \n\t"
"Lmoveven_%=: \n\t"
" subi %A[num], 2 \n\t"
" brcc Lmovloop_%= \n\t"
" sbci %B[num], 0 \n\t"
" brcc Lmovloop_%= \n\t"
: [num] "+r" (num)
: [src] "r" (src),
[dst] "r" (dst)
: "memory"
);
return dst;
}
}
} /* end extern "C" */
#endif /* AVR */
#if 0
// TEST / VERIFICATION CODE ONLY BELOW THIS POINT
#include <Arduino.h>
#include "lib8tion.h"
void test1abs( int8_t i)
{
Serial.print("abs("); Serial.print(i); Serial.print(") = ");
int8_t j = abs8(i);
Serial.print(j); Serial.println(" ");
}
void testabs()
{
delay(5000);
for( int8_t q = -128; q != 127; ++q) {
test1abs(q);
}
for(;;){};
}
void testmul8()
{
delay(5000);
byte r, c;
Serial.println("mul8:");
for( r = 0; r <= 20; r += 1) {
Serial.print(r); Serial.print(" : ");
for( c = 0; c <= 20; c += 1) {
byte t;
t = mul8( r, c);
Serial.print(t); Serial.print(' ');
}
Serial.println(' ');
}
Serial.println("done.");
for(;;){};
}
void testscale8()
{
delay(5000);
byte r, c;
Serial.println("scale8:");
for( r = 0; r <= 240; r += 10) {
Serial.print(r); Serial.print(" : ");
for( c = 0; c <= 240; c += 10) {
byte t;
t = scale8( r, c);
Serial.print(t); Serial.print(' ');
}
Serial.println(' ');
}
Serial.println(' ');
Serial.println("scale8_video:");
for( r = 0; r <= 100; r += 4) {
Serial.print(r); Serial.print(" : ");
for( c = 0; c <= 100; c += 4) {
byte t;
t = scale8_video( r, c);
Serial.print(t); Serial.print(' ');
}
Serial.println(' ');
}
Serial.println("done.");
for(;;){};
}
void testqadd8()
{
delay(5000);
byte r, c;
for( r = 0; r <= 240; r += 10) {
Serial.print(r); Serial.print(" : ");
for( c = 0; c <= 240; c += 10) {
byte t;
t = qadd8( r, c);
Serial.print(t); Serial.print(' ');
}
Serial.println(' ');
}
Serial.println("done.");
for(;;){};
}
void testnscale8x3()
{
delay(5000);
byte r, g, b, sc;
for( byte z = 0; z < 10; ++z) {
r = random8(); g = random8(); b = random8(); sc = random8();
Serial.print("nscale8x3_video( ");
Serial.print(r); Serial.print(", ");
Serial.print(g); Serial.print(", ");
Serial.print(b); Serial.print(", ");
Serial.print(sc); Serial.print(") = [ ");
nscale8x3_video( r, g, b, sc);
Serial.print(r); Serial.print(", ");
Serial.print(g); Serial.print(", ");
Serial.print(b); Serial.print("]");
Serial.println(' ');
}
Serial.println("done.");
for(;;){};
}
#endif
FASTLED_NAMESPACE_END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,557 @@
#ifndef __INC_LIB8TION_MATH_H
#define __INC_LIB8TION_MATH_H
#include "scale8.h"
///@ingroup lib8tion
///@defgroup Math Basic math operations
/// Fast, efficient 8-bit math functions specifically
/// designed for high-performance LED programming.
///
/// Because of the AVR(Arduino) and ARM assembly language
/// implementations provided, using these functions often
/// results in smaller and faster code than the equivalent
/// program using plain "C" arithmetic and logic.
///@{
/// add one byte to another, saturating at 0xFF
/// @param i - first byte to add
/// @param j - second byte to add
/// @returns the sum of i & j, capped at 0xFF
LIB8STATIC_ALWAYS_INLINE uint8_t qadd8( uint8_t i, uint8_t j)
{
#if QADD8_C == 1
unsigned int t = i + j;
if( t > 255) t = 255;
return t;
#elif QADD8_AVRASM == 1
asm volatile(
/* First, add j to i, conditioning the C flag */
"add %0, %1 \n\t"
/* Now test the C flag.
If C is clear, we branch around a load of 0xFF into i.
If C is set, we go ahead and load 0xFF into i.
*/
"brcc L_%= \n\t"
"ldi %0, 0xFF \n\t"
"L_%=: "
: "+a" (i)
: "a" (j)
);
return i;
#elif QADD8_ARM_DSP_ASM == 1
asm volatile( "uqadd8 %0, %0, %1" : "+r" (i) : "r" (j));
return i;
#else
#error "No implementation for qadd8 available."
#endif
}
/// Add one byte to another, saturating at 0x7F
/// @param i - first byte to add
/// @param j - second byte to add
/// @returns the sum of i & j, capped at 0xFF
LIB8STATIC_ALWAYS_INLINE int8_t qadd7( int8_t i, int8_t j)
{
#if QADD7_C == 1
int16_t t = i + j;
if( t > 127) t = 127;
return t;
#elif QADD7_AVRASM == 1
asm volatile(
/* First, add j to i, conditioning the V flag */
"add %0, %1 \n\t"
/* Now test the V flag.
If V is clear, we branch around a load of 0x7F into i.
If V is set, we go ahead and load 0x7F into i.
*/
"brvc L_%= \n\t"
"ldi %0, 0x7F \n\t"
"L_%=: "
: "+a" (i)
: "a" (j)
);
return i;
#elif QADD7_ARM_DSP_ASM == 1
asm volatile( "qadd8 %0, %0, %1" : "+r" (i) : "r" (j));
return i;
#else
#error "No implementation for qadd7 available."
#endif
}
/// subtract one byte from another, saturating at 0x00
/// @returns i - j with a floor of 0
LIB8STATIC_ALWAYS_INLINE uint8_t qsub8( uint8_t i, uint8_t j)
{
#if QSUB8_C == 1
int t = i - j;
if( t < 0) t = 0;
return t;
#elif QSUB8_AVRASM == 1
asm volatile(
/* First, subtract j from i, conditioning the C flag */
"sub %0, %1 \n\t"
/* Now test the C flag.
If C is clear, we branch around a load of 0x00 into i.
If C is set, we go ahead and load 0x00 into i.
*/
"brcc L_%= \n\t"
"ldi %0, 0x00 \n\t"
"L_%=: "
: "+a" (i)
: "a" (j)
);
return i;
#else
#error "No implementation for qsub8 available."
#endif
}
/// add one byte to another, with one byte result
LIB8STATIC_ALWAYS_INLINE uint8_t add8( uint8_t i, uint8_t j)
{
#if ADD8_C == 1
int t = i + j;
return t;
#elif ADD8_AVRASM == 1
// Add j to i, period.
asm volatile( "add %0, %1" : "+a" (i) : "a" (j));
return i;
#else
#error "No implementation for add8 available."
#endif
}
/// add one byte to another, with one byte result
LIB8STATIC_ALWAYS_INLINE uint16_t add8to16( uint8_t i, uint16_t j)
{
#if ADD8_C == 1
uint16_t t = i + j;
return t;
#elif ADD8_AVRASM == 1
// Add i(one byte) to j(two bytes)
asm volatile(
"add %A[j], %[i] \n\t"
"adc %B[j], __zero_reg__ \n\t"
: [j] "+a" (j)
: [i] "a" (i)
);
return i;
#else
#error "No implementation for add8to16 available."
#endif
}
/// subtract one byte from another, 8-bit result
LIB8STATIC_ALWAYS_INLINE uint8_t sub8( uint8_t i, uint8_t j)
{
#if SUB8_C == 1
int t = i - j;
return t;
#elif SUB8_AVRASM == 1
// Subtract j from i, period.
asm volatile( "sub %0, %1" : "+a" (i) : "a" (j));
return i;
#else
#error "No implementation for sub8 available."
#endif
}
/// Calculate an integer average of two unsigned
/// 8-bit integer values (uint8_t).
/// Fractional results are rounded down, e.g. avg8(20,41) = 30
LIB8STATIC_ALWAYS_INLINE uint8_t avg8( uint8_t i, uint8_t j)
{
#if AVG8_C == 1
return (i + j) >> 1;
#elif AVG8_AVRASM == 1
asm volatile(
/* First, add j to i, 9th bit overflows into C flag */
"add %0, %1 \n\t"
/* Divide by two, moving C flag into high 8th bit */
"ror %0 \n\t"
: "+a" (i)
: "a" (j)
);
return i;
#else
#error "No implementation for avg8 available."
#endif
}
/// Calculate an integer average of two unsigned
/// 16-bit integer values (uint16_t).
/// Fractional results are rounded down, e.g. avg16(20,41) = 30
LIB8STATIC_ALWAYS_INLINE uint16_t avg16( uint16_t i, uint16_t j)
{
#if AVG16_C == 1
return (uint32_t)((uint32_t)(i) + (uint32_t)(j)) >> 1;
#elif AVG16_AVRASM == 1
asm volatile(
/* First, add jLo (heh) to iLo, 9th bit overflows into C flag */
"add %A[i], %A[j] \n\t"
/* Now, add C + jHi to iHi, 17th bit overflows into C flag */
"adc %B[i], %B[j] \n\t"
/* Divide iHi by two, moving C flag into high 16th bit, old 9th bit now in C */
"ror %B[i] \n\t"
/* Divide iLo by two, moving C flag into high 8th bit */
"ror %A[i] \n\t"
: [i] "+a" (i)
: [j] "a" (j)
);
return i;
#else
#error "No implementation for avg16 available."
#endif
}
/// Calculate an integer average of two signed 7-bit
/// integers (int8_t)
/// If the first argument is even, result is rounded down.
/// If the first argument is odd, result is result up.
LIB8STATIC_ALWAYS_INLINE int8_t avg7( int8_t i, int8_t j)
{
#if AVG7_C == 1
return ((i + j) >> 1) + (i & 0x1);
#elif AVG7_AVRASM == 1
asm volatile(
"asr %1 \n\t"
"asr %0 \n\t"
"adc %0, %1 \n\t"
: "+a" (i)
: "a" (j)
);
return i;
#else
#error "No implementation for avg7 available."
#endif
}
/// Calculate an integer average of two signed 15-bit
/// integers (int16_t)
/// If the first argument is even, result is rounded down.
/// If the first argument is odd, result is result up.
LIB8STATIC_ALWAYS_INLINE int16_t avg15( int16_t i, int16_t j)
{
#if AVG15_C == 1
return ((int32_t)((int32_t)(i) + (int32_t)(j)) >> 1) + (i & 0x1);
#elif AVG15_AVRASM == 1
asm volatile(
/* first divide j by 2, throwing away lowest bit */
"asr %B[j] \n\t"
"ror %A[j] \n\t"
/* now divide i by 2, with lowest bit going into C */
"asr %B[i] \n\t"
"ror %A[i] \n\t"
/* add j + C to i */
"adc %A[i], %A[j] \n\t"
"adc %B[i], %B[j] \n\t"
: [i] "+a" (i)
: [j] "a" (j)
);
return i;
#else
#error "No implementation for avg15 available."
#endif
}
/// Calculate the remainder of one unsigned 8-bit
/// value divided by anoter, aka A % M.
/// Implemented by repeated subtraction, which is
/// very compact, and very fast if A is 'probably'
/// less than M. If A is a large multiple of M,
/// the loop has to execute multiple times. However,
/// even in that case, the loop is only two
/// instructions long on AVR, i.e., quick.
LIB8STATIC_ALWAYS_INLINE uint8_t mod8( uint8_t a, uint8_t m)
{
#if defined(__AVR__)
asm volatile (
"L_%=: sub %[a],%[m] \n\t"
" brcc L_%= \n\t"
" add %[a],%[m] \n\t"
: [a] "+r" (a)
: [m] "r" (m)
);
#else
while( a >= m) a -= m;
#endif
return a;
}
/// Add two numbers, and calculate the modulo
/// of the sum and a third number, M.
/// In other words, it returns (A+B) % M.
/// It is designed as a compact mechanism for
/// incrementing a 'mode' switch and wrapping
/// around back to 'mode 0' when the switch
/// goes past the end of the available range.
/// e.g. if you have seven modes, this switches
/// to the next one and wraps around if needed:
/// mode = addmod8( mode, 1, 7);
///LIB8STATIC_ALWAYS_INLINESee 'mod8' for notes on performance.
LIB8STATIC uint8_t addmod8( uint8_t a, uint8_t b, uint8_t m)
{
#if defined(__AVR__)
asm volatile (
" add %[a],%[b] \n\t"
"L_%=: sub %[a],%[m] \n\t"
" brcc L_%= \n\t"
" add %[a],%[m] \n\t"
: [a] "+r" (a)
: [b] "r" (b), [m] "r" (m)
);
#else
a += b;
while( a >= m) a -= m;
#endif
return a;
}
/// Subtract two numbers, and calculate the modulo
/// of the difference and a third number, M.
/// In other words, it returns (A-B) % M.
/// It is designed as a compact mechanism for
/// incrementing a 'mode' switch and wrapping
/// around back to 'mode 0' when the switch
/// goes past the end of the available range.
/// e.g. if you have seven modes, this switches
/// to the next one and wraps around if needed:
/// mode = addmod8( mode, 1, 7);
///LIB8STATIC_ALWAYS_INLINESee 'mod8' for notes on performance.
LIB8STATIC uint8_t submod8( uint8_t a, uint8_t b, uint8_t m)
{
#if defined(__AVR__)
asm volatile (
" sub %[a],%[b] \n\t"
"L_%=: sub %[a],%[m] \n\t"
" brcc L_%= \n\t"
" add %[a],%[m] \n\t"
: [a] "+r" (a)
: [b] "r" (b), [m] "r" (m)
);
#else
a -= b;
while( a >= m) a -= m;
#endif
return a;
}
/// 8x8 bit multiplication, with 8 bit result
LIB8STATIC_ALWAYS_INLINE uint8_t mul8( uint8_t i, uint8_t j)
{
#if MUL8_C == 1
return ((int)i * (int)(j) ) & 0xFF;
#elif MUL8_AVRASM == 1
asm volatile(
/* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
"mul %0, %1 \n\t"
/* Extract the LOW 8-bits (r0) */
"mov %0, r0 \n\t"
/* Restore r1 to "0"; it's expected to always be that */
"clr __zero_reg__ \n\t"
: "+a" (i)
: "a" (j)
: "r0", "r1"
);
return i;
#else
#error "No implementation for mul8 available."
#endif
}
/// saturating 8x8 bit multiplication, with 8 bit result
/// @returns the product of i * j, capping at 0xFF
LIB8STATIC_ALWAYS_INLINE uint8_t qmul8( uint8_t i, uint8_t j)
{
#if QMUL8_C == 1
int p = ((int)i * (int)(j) );
if( p > 255) p = 255;
return p;
#elif QMUL8_AVRASM == 1
asm volatile(
/* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
" mul %0, %1 \n\t"
/* If high byte of result is zero, all is well. */
" tst r1 \n\t"
" breq Lnospill_%= \n\t"
/* If high byte of result > 0, saturate low byte to 0xFF */
" ldi %0,0xFF \n\t"
" rjmp Ldone_%= \n\t"
"Lnospill_%=: \n\t"
/* Extract the LOW 8-bits (r0) */
" mov %0, r0 \n\t"
"Ldone_%=: \n\t"
/* Restore r1 to "0"; it's expected to always be that */
" clr __zero_reg__ \n\t"
: "+a" (i)
: "a" (j)
: "r0", "r1"
);
return i;
#else
#error "No implementation for qmul8 available."
#endif
}
/// take abs() of a signed 8-bit uint8_t
LIB8STATIC_ALWAYS_INLINE int8_t abs8( int8_t i)
{
#if ABS8_C == 1
if( i < 0) i = -i;
return i;
#elif ABS8_AVRASM == 1
asm volatile(
/* First, check the high bit, and prepare to skip if it's clear */
"sbrc %0, 7 \n"
/* Negate the value */
"neg %0 \n"
: "+r" (i) : "r" (i)
);
return i;
#else
#error "No implementation for abs8 available."
#endif
}
/// square root for 16-bit integers
/// About three times faster and five times smaller
/// than Arduino's general sqrt on AVR.
LIB8STATIC uint8_t sqrt16(uint16_t x)
{
if( x <= 1) {
return x;
}
uint8_t low = 1; // lower bound
uint8_t hi, mid;
if( x > 7904) {
hi = 255;
} else {
hi = (x >> 5) + 8; // initial estimate for upper bound
}
do {
mid = (low + hi) >> 1;
if ((uint16_t)(mid * mid) > x) {
hi = mid - 1;
} else {
if( mid == 255) {
return 255;
}
low = mid + 1;
}
} while (hi >= low);
return low - 1;
}
/// blend a variable proproportion(0-255) of one byte to another
/// @param a - the starting byte value
/// @param b - the byte value to blend toward
/// @param amountOfB - the proportion (0-255) of b to blend
/// @returns a byte value between a and b, inclusive
#if (FASTLED_BLEND_FIXED == 1)
LIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB)
{
#if BLEND8_C == 1
uint16_t partial;
uint8_t result;
uint8_t amountOfA = 255 - amountOfB;
partial = (a * amountOfA);
#if (FASTLED_SCALE8_FIXED == 1)
partial += a;
//partial = add8to16( a, partial);
#endif
partial += (b * amountOfB);
#if (FASTLED_SCALE8_FIXED == 1)
partial += b;
//partial = add8to16( b, partial);
#endif
result = partial >> 8;
return result;
#elif BLEND8_AVRASM == 1
uint16_t partial;
uint8_t result;
asm volatile (
/* partial = b * amountOfB */
" mul %[b], %[amountOfB] \n\t"
" movw %A[partial], r0 \n\t"
/* amountOfB (aka amountOfA) = 255 - amountOfB */
" com %[amountOfB] \n\t"
/* partial += a * amountOfB (aka amountOfA) */
" mul %[a], %[amountOfB] \n\t"
" add %A[partial], r0 \n\t"
" adc %B[partial], r1 \n\t"
" clr __zero_reg__ \n\t"
#if (FASTLED_SCALE8_FIXED == 1)
/* partial += a */
" add %A[partial], %[a] \n\t"
" adc %B[partial], __zero_reg__ \n\t"
// partial += b
" add %A[partial], %[b] \n\t"
" adc %B[partial], __zero_reg__ \n\t"
#endif
: [partial] "=r" (partial),
[amountOfB] "+a" (amountOfB)
: [a] "a" (a),
[b] "a" (b)
: "r0", "r1"
);
result = partial >> 8;
return result;
#else
#error "No implementation for blend8 available."
#endif
}
#else
LIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB)
{
// This version loses precision in the integer math
// and can actually return results outside of the range
// from a to b. Its use is not recommended.
uint8_t result;
uint8_t amountOfA = 255 - amountOfB;
result = scale8_LEAVING_R1_DIRTY( a, amountOfA)
+ scale8_LEAVING_R1_DIRTY( b, amountOfB);
cleanup_R1();
return result;
}
#endif
///@}
#endif

View File

@@ -0,0 +1,100 @@
#ifndef __INC_LIB8TION_RANDOM_H
#define __INC_LIB8TION_RANDOM_H
///@ingroup lib8tion
///@defgroup Random Fast random number generators
/// Fast 8- and 16- bit unsigned random numbers.
/// Significantly faster than Arduino random(), but
/// also somewhat less random. You can add entropy.
///@{
// X(n+1) = (2053 * X(n)) + 13849)
#define FASTLED_RAND16_2053 ((uint16_t)(2053))
#define FASTLED_RAND16_13849 ((uint16_t)(13849))
#if defined(LIB8_ATTINY)
#define APPLY_FASTLED_RAND16_2053(x) (x << 11) + (x << 2) + x
#else
#define APPLY_FASTLED_RAND16_2053(x) (x * FASTLED_RAND16_2053)
#endif
/// random number seed
extern uint16_t rand16seed;// = RAND16_SEED;
/// Generate an 8-bit random number
LIB8STATIC uint8_t random8()
{
rand16seed = APPLY_FASTLED_RAND16_2053(rand16seed) + FASTLED_RAND16_13849;
// return the sum of the high and low bytes, for better
// mixing and non-sequential correlation
return (uint8_t)(((uint8_t)(rand16seed & 0xFF)) +
((uint8_t)(rand16seed >> 8)));
}
/// Generate a 16 bit random number
LIB8STATIC uint16_t random16()
{
rand16seed = APPLY_FASTLED_RAND16_2053(rand16seed) + FASTLED_RAND16_13849;
return rand16seed;
}
/// Generate an 8-bit random number between 0 and lim
/// @param lim the upper bound for the result
LIB8STATIC uint8_t random8(uint8_t lim)
{
uint8_t r = random8();
r = (r*lim) >> 8;
return r;
}
/// Generate an 8-bit random number in the given range
/// @param min the lower bound for the random number
/// @param lim the upper bound for the random number
LIB8STATIC uint8_t random8(uint8_t min, uint8_t lim)
{
uint8_t delta = lim - min;
uint8_t r = random8(delta) + min;
return r;
}
/// Generate an 16-bit random number between 0 and lim
/// @param lim the upper bound for the result
LIB8STATIC uint16_t random16( uint16_t lim)
{
uint16_t r = random16();
uint32_t p = (uint32_t)lim * (uint32_t)r;
r = p >> 16;
return r;
}
/// Generate an 16-bit random number in the given range
/// @param min the lower bound for the random number
/// @param lim the upper bound for the random number
LIB8STATIC uint16_t random16( uint16_t min, uint16_t lim)
{
uint16_t delta = lim - min;
uint16_t r = random16( delta) + min;
return r;
}
/// Set the 16-bit seed used for the random number generator
LIB8STATIC void random16_set_seed( uint16_t seed)
{
rand16seed = seed;
}
/// Get the current seed value for the random number generator
LIB8STATIC uint16_t random16_get_seed()
{
return rand16seed;
}
/// Add entropy into the random number generator
LIB8STATIC void random16_add_entropy( uint16_t entropy)
{
rand16seed += entropy;
}
///@}
#endif

View File

@@ -0,0 +1,709 @@
#ifndef __INC_LIB8TION_SCALE_H
#define __INC_LIB8TION_SCALE_H
///@ingroup lib8tion
///@defgroup Scaling Scaling functions
/// Fast, efficient 8-bit scaling functions specifically
/// designed for high-performance LED programming.
///
/// Because of the AVR(Arduino) and ARM assembly language
/// implementations provided, using these functions often
/// results in smaller and faster code than the equivalent
/// program using plain "C" arithmetic and logic.
///@{
/// scale one byte by a second one, which is treated as
/// the numerator of a fraction whose denominator is 256
/// In other words, it computes i * (scale / 256)
/// 4 clocks AVR with MUL, 2 clocks ARM
LIB8STATIC_ALWAYS_INLINE uint8_t scale8( uint8_t i, fract8 scale)
{
#if SCALE8_C == 1
#if (FASTLED_SCALE8_FIXED == 1)
return (((uint16_t)i) * (1+(uint16_t)(scale))) >> 8;
#else
return ((uint16_t)i * (uint16_t)(scale) ) >> 8;
#endif
#elif SCALE8_AVRASM == 1
#if defined(LIB8_ATTINY)
#if (FASTLED_SCALE8_FIXED == 1)
uint8_t work=i;
#else
uint8_t work=0;
#endif
uint8_t cnt=0x80;
asm volatile(
#if (FASTLED_SCALE8_FIXED == 1)
" inc %[scale] \n\t"
" breq DONE_%= \n\t"
" clr %[work] \n\t"
#endif
"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_%= \n\t"
"DONE_%=: \n\t"
: [work] "+r" (work), [cnt] "+r" (cnt)
: [scale] "r" (scale), [i] "r" (i)
:
);
return work;
#else
asm volatile(
#if (FASTLED_SCALE8_FIXED==1)
// Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
"mul %0, %1 \n\t"
// Add i to r0, possibly setting the carry flag
"add r0, %0 \n\t"
// load the immediate 0 into i (note, this does _not_ touch any flags)
"ldi %0, 0x00 \n\t"
// walk and chew gum at the same time
"adc %0, r1 \n\t"
#else
/* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
"mul %0, %1 \n\t"
/* Move the high 8-bits of the product (r1) back to i */
"mov %0, r1 \n\t"
/* Restore r1 to "0"; it's expected to always be that */
#endif
"clr __zero_reg__ \n\t"
: "+a" (i) /* writes to i */
: "a" (scale) /* uses scale */
: "r0", "r1" /* clobbers r0, r1 */
);
/* Return the result */
return i;
#endif
#else
#error "No implementation for scale8 available."
#endif
}
/// The "video" version of scale8 guarantees that the output will
/// be only be zero if one or both of the inputs are zero. If both
/// inputs are non-zero, the output is guaranteed to be non-zero.
/// This makes for better 'video'/LED dimming, at the cost of
/// several additional cycles.
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video( uint8_t i, fract8 scale)
{
#if SCALE8_C == 1 || defined(LIB8_ATTINY)
uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
// uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
// uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
return j;
#elif SCALE8_AVRASM == 1
uint8_t j=0;
asm volatile(
" tst %[i]\n\t"
" breq L_%=\n\t"
" mul %[i], %[scale]\n\t"
" mov %[j], r1\n\t"
" clr __zero_reg__\n\t"
" cpse %[scale], r1\n\t"
" subi %[j], 0xFF\n\t"
"L_%=: \n\t"
: [j] "+a" (j)
: [i] "a" (i), [scale] "a" (scale)
: "r0", "r1"
);
return j;
// uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
// asm volatile(
// " tst %0 \n"
// " breq L_%= \n"
// " mul %0, %1 \n"
// " mov %0, r1 \n"
// " add %0, %2 \n"
// " clr __zero_reg__ \n"
// "L_%=: \n"
// : "+a" (i)
// : "a" (scale), "a" (nonzeroscale)
// : "r0", "r1");
// // Return the result
// return i;
#else
#error "No implementation for scale8_video available."
#endif
}
/// This version of scale8 does not clean up the R1 register on AVR
/// If you are doing several 'scale8's in a row, use this, and
/// then explicitly call cleanup_R1.
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
{
#if SCALE8_C == 1
#if (FASTLED_SCALE8_FIXED == 1)
return (((uint16_t)i) * ((uint16_t)(scale)+1)) >> 8;
#else
return ((int)i * (int)(scale) ) >> 8;
#endif
#elif SCALE8_AVRASM == 1
asm volatile(
#if (FASTLED_SCALE8_FIXED==1)
// Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
"mul %0, %1 \n\t"
// Add i to r0, possibly setting the carry flag
"add r0, %0 \n\t"
// load the immediate 0 into i (note, this does _not_ touch any flags)
"ldi %0, 0x00 \n\t"
// walk and chew gum at the same time
"adc %0, r1 \n\t"
#else
/* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
"mul %0, %1 \n\t"
/* Move the high 8-bits of the product (r1) back to i */
"mov %0, r1 \n\t"
#endif
/* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */
/* "clr __zero_reg__ \n\t" */
: "+a" (i) /* writes to i */
: "a" (scale) /* uses scale */
: "r0", "r1" /* clobbers r0, r1 */
);
// Return the result
return i;
#else
#error "No implementation for scale8_LEAVING_R1_DIRTY available."
#endif
}
/// In place modifying version of scale8, also this version of nscale8 does not
/// clean up the R1 register on AVR
/// If you are doing several 'scale8's in a row, use this, and
/// then explicitly call cleanup_R1.
LIB8STATIC_ALWAYS_INLINE void nscale8_LEAVING_R1_DIRTY( uint8_t& i, fract8 scale)
{
#if SCALE8_C == 1
#if (FASTLED_SCALE8_FIXED == 1)
i = (((uint16_t)i) * ((uint16_t)(scale)+1)) >> 8;
#else
i = ((int)i * (int)(scale) ) >> 8;
#endif
#elif SCALE8_AVRASM == 1
asm volatile(
#if (FASTLED_SCALE8_FIXED==1)
// Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
"mul %0, %1 \n\t"
// Add i to r0, possibly setting the carry flag
"add r0, %0 \n\t"
// load the immediate 0 into i (note, this does _not_ touch any flags)
"ldi %0, 0x00 \n\t"
// walk and chew gum at the same time
"adc %0, r1 \n\t"
#else
/* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
"mul %0, %1 \n\t"
/* Move the high 8-bits of the product (r1) back to i */
"mov %0, r1 \n\t"
#endif
/* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */
/* "clr __zero_reg__ \n\t" */
: "+a" (i) /* writes to i */
: "a" (scale) /* uses scale */
: "r0", "r1" /* clobbers r0, r1 */
);
#else
#error "No implementation for nscale8_LEAVING_R1_DIRTY available."
#endif
}
/// This version of scale8_video does not clean up the R1 register on AVR
/// If you are doing several 'scale8_video's in a row, use this, and
/// then explicitly call cleanup_R1.
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
{
#if SCALE8_C == 1 || defined(LIB8_ATTINY)
uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
// uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
// uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
return j;
#elif SCALE8_AVRASM == 1
uint8_t j=0;
asm volatile(
" tst %[i]\n\t"
" breq L_%=\n\t"
" mul %[i], %[scale]\n\t"
" mov %[j], r1\n\t"
" breq L_%=\n\t"
" subi %[j], 0xFF\n\t"
"L_%=: \n\t"
: [j] "+a" (j)
: [i] "a" (i), [scale] "a" (scale)
: "r0", "r1"
);
return j;
// uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
// asm volatile(
// " tst %0 \n"
// " breq L_%= \n"
// " mul %0, %1 \n"
// " mov %0, r1 \n"
// " add %0, %2 \n"
// " clr __zero_reg__ \n"
// "L_%=: \n"
// : "+a" (i)
// : "a" (scale), "a" (nonzeroscale)
// : "r0", "r1");
// // Return the result
// return i;
#else
#error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
#endif
}
/// In place modifying version of scale8_video, also this version of nscale8_video
/// does not clean up the R1 register on AVR
/// If you are doing several 'scale8_video's in a row, use this, and
/// then explicitly call cleanup_R1.
LIB8STATIC_ALWAYS_INLINE void nscale8_video_LEAVING_R1_DIRTY( uint8_t & i, fract8 scale)
{
#if SCALE8_C == 1 || defined(LIB8_ATTINY)
i = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
#elif SCALE8_AVRASM == 1
asm volatile(
" tst %[i]\n\t"
" breq L_%=\n\t"
" mul %[i], %[scale]\n\t"
" mov %[i], r1\n\t"
" breq L_%=\n\t"
" subi %[i], 0xFF\n\t"
"L_%=: \n\t"
: [i] "+a" (i)
: [scale] "a" (scale)
: "r0", "r1"
);
#else
#error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
#endif
}
/// Clean up the r1 register after a series of *LEAVING_R1_DIRTY calls
LIB8STATIC_ALWAYS_INLINE void cleanup_R1()
{
#if CLEANUP_R1_AVRASM == 1
// Restore r1 to "0"; it's expected to always be that
asm volatile( "clr __zero_reg__ \n\t" : : : "r1" );
#endif
}
/// scale three one byte values by a fourth one, which is treated as
/// the numerator of a fraction whose demominator is 256
/// In other words, it computes r,g,b * (scale / 256)
///
/// THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENTS IN PLACE
LIB8STATIC void nscale8x3( uint8_t& r, uint8_t& g, uint8_t& b, fract8 scale)
{
#if SCALE8_C == 1
#if (FASTLED_SCALE8_FIXED == 1)
uint16_t scale_fixed = scale + 1;
r = (((uint16_t)r) * scale_fixed) >> 8;
g = (((uint16_t)g) * scale_fixed) >> 8;
b = (((uint16_t)b) * scale_fixed) >> 8;
#else
r = ((int)r * (int)(scale) ) >> 8;
g = ((int)g * (int)(scale) ) >> 8;
b = ((int)b * (int)(scale) ) >> 8;
#endif
#elif SCALE8_AVRASM == 1
r = scale8_LEAVING_R1_DIRTY(r, scale);
g = scale8_LEAVING_R1_DIRTY(g, scale);
b = scale8_LEAVING_R1_DIRTY(b, scale);
cleanup_R1();
#else
#error "No implementation for nscale8x3 available."
#endif
}
/// scale three one byte values by a fourth one, which is treated as
/// the numerator of a fraction whose demominator is 256
/// In other words, it computes r,g,b * (scale / 256), ensuring
/// that non-zero values passed in remain non zero, no matter how low the scale
/// argument.
///
/// THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENTS IN PLACE
LIB8STATIC void nscale8x3_video( uint8_t& r, uint8_t& g, uint8_t& b, fract8 scale)
{
#if SCALE8_C == 1
uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
r = (r == 0) ? 0 : (((int)r * (int)(scale) ) >> 8) + nonzeroscale;
g = (g == 0) ? 0 : (((int)g * (int)(scale) ) >> 8) + nonzeroscale;
b = (b == 0) ? 0 : (((int)b * (int)(scale) ) >> 8) + nonzeroscale;
#elif SCALE8_AVRASM == 1
nscale8_video_LEAVING_R1_DIRTY( r, scale);
nscale8_video_LEAVING_R1_DIRTY( g, scale);
nscale8_video_LEAVING_R1_DIRTY( b, scale);
cleanup_R1();
#else
#error "No implementation for nscale8x3 available."
#endif
}
/// scale two one byte values by a third one, which is treated as
/// the numerator of a fraction whose demominator is 256
/// In other words, it computes i,j * (scale / 256)
///
/// THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENTS IN PLACE
LIB8STATIC void nscale8x2( uint8_t& i, uint8_t& j, fract8 scale)
{
#if SCALE8_C == 1
#if FASTLED_SCALE8_FIXED == 1
uint16_t scale_fixed = scale + 1;
i = (((uint16_t)i) * scale_fixed ) >> 8;
j = (((uint16_t)j) * scale_fixed ) >> 8;
#else
i = ((uint16_t)i * (uint16_t)(scale) ) >> 8;
j = ((uint16_t)j * (uint16_t)(scale) ) >> 8;
#endif
#elif SCALE8_AVRASM == 1
i = scale8_LEAVING_R1_DIRTY(i, scale);
j = scale8_LEAVING_R1_DIRTY(j, scale);
cleanup_R1();
#else
#error "No implementation for nscale8x2 available."
#endif
}
/// scale two one byte values by a third one, which is treated as
/// the numerator of a fraction whose demominator is 256
/// In other words, it computes i,j * (scale / 256), ensuring
/// that non-zero values passed in remain non zero, no matter how low the scale
/// argument.
///
/// THIS FUNCTION ALWAYS MODIFIES ITS ARGUMENTS IN PLACE
LIB8STATIC void nscale8x2_video( uint8_t& i, uint8_t& j, fract8 scale)
{
#if SCALE8_C == 1
uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
i = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
j = (j == 0) ? 0 : (((int)j * (int)(scale) ) >> 8) + nonzeroscale;
#elif SCALE8_AVRASM == 1
nscale8_video_LEAVING_R1_DIRTY( i, scale);
nscale8_video_LEAVING_R1_DIRTY( j, scale);
cleanup_R1();
#else
#error "No implementation for nscale8x2 available."
#endif
}
/// scale a 16-bit unsigned value by an 8-bit value,
/// considered as numerator of a fraction whose denominator
/// is 256. In other words, it computes i * (scale / 256)
LIB8STATIC_ALWAYS_INLINE uint16_t scale16by8( uint16_t i, fract8 scale )
{
#if SCALE16BY8_C == 1
uint16_t result;
#if FASTLED_SCALE8_FIXED == 1
result = (i * (1+((uint16_t)scale))) >> 8;
#else
result = (i * scale) / 256;
#endif
return result;
#elif SCALE16BY8_AVRASM == 1
#if FASTLED_SCALE8_FIXED == 1
uint16_t result = 0;
asm volatile(
// result.A = HighByte( (i.A x scale) + i.A )
" mul %A[i], %[scale] \n\t"
" add r0, %A[i] \n\t"
// " adc r1, [zero] \n\t"
// " mov %A[result], r1 \n\t"
" adc %A[result], r1 \n\t"
// result.A-B += i.B x scale
" 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.A-B += i.B
" add %A[result], %B[i] \n\t"
" adc %B[result], __zero_reg__ \n\t"
: [result] "+r" (result)
: [i] "r" (i), [scale] "r" (scale)
: "r0", "r1"
);
return result;
#else
uint16_t result = 0;
asm volatile(
// result.A = HighByte(i.A x j )
" mul %A[i], %[scale] \n\t"
" mov %A[result], r1 \n\t"
//" clr %B[result] \n\t"
// result.A-B += i.B x j
" mul %B[i], %[scale] \n\t"
" add %A[result], r0 \n\t"
" adc %B[result], r1 \n\t"
// cleanup r1
" clr __zero_reg__ \n\t"
: [result] "+r" (result)
: [i] "r" (i), [scale] "r" (scale)
: "r0", "r1"
);
return result;
#endif
#else
#error "No implementation for scale16by8 available."
#endif
}
/// scale a 16-bit unsigned value by a 16-bit value,
/// considered as numerator of a fraction whose denominator
/// is 65536. In other words, it computes i * (scale / 65536)
LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale )
{
#if SCALE16_C == 1
uint16_t result;
#if FASTLED_SCALE8_FIXED == 1
result = ((uint32_t)(i) * (1+(uint32_t)(scale))) / 65536;
#else
result = ((uint32_t)(i) * (uint32_t)(scale)) / 65536;
#endif
return result;
#elif SCALE16_AVRASM == 1
#if FASTLED_SCALE8_FIXED == 1
// implemented sort of like
// result = ((i * scale) + i ) / 65536
//
// why not like this, you may ask?
// result = (i * (scale+1)) / 65536
// the answer is that if scale is 65535, then scale+1
// will be zero, which is not what we want.
uint32_t result;
asm volatile(
// result.A-B = i.A x scale.A
" mul %A[i], %A[scale] \n\t"
// save results...
// basic idea:
//" mov %A[result], r0 \n\t"
//" mov %B[result], r1 \n\t"
// which can be written as...
" movw %A[result], r0 \n\t"
// Because we're going to add i.A-B to
// result.A-D, we DO need to keep both
// the r0 and r1 portions of the product
// UNlike in the 'unfixed scale8' version.
// So the movw here is needed.
: [result] "=r" (result)
: [i] "r" (i),
[scale] "r" (scale)
: "r0", "r1"
);
asm volatile(
// result.C-D = i.B x scale.B
" mul %B[i], %B[scale] \n\t"
//" mov %C[result], r0 \n\t"
//" mov %D[result], r1 \n\t"
" movw %C[result], r0 \n\t"
: [result] "+r" (result)
: [i] "r" (i),
[scale] "r" (scale)
: "r0", "r1"
);
const uint8_t zero = 0;
asm volatile(
// result.B-D += i.B x scale.A
" mul %B[i], %A[scale] \n\t"
" add %B[result], r0 \n\t"
" adc %C[result], r1 \n\t"
" adc %D[result], %[zero] \n\t"
// result.B-D += i.A x scale.B
" mul %A[i], %B[scale] \n\t"
" add %B[result], r0 \n\t"
" adc %C[result], r1 \n\t"
" adc %D[result], %[zero] \n\t"
// cleanup r1
" clr r1 \n\t"
: [result] "+r" (result)
: [i] "r" (i),
[scale] "r" (scale),
[zero] "r" (zero)
: "r0", "r1"
);
asm volatile(
// result.A-D += i.A-B
" add %A[result], %A[i] \n\t"
" adc %B[result], %B[i] \n\t"
" adc %C[result], %[zero] \n\t"
" adc %D[result], %[zero] \n\t"
: [result] "+r" (result)
: [i] "r" (i),
[zero] "r" (zero)
);
result = result >> 16;
return result;
#else
uint32_t result;
asm volatile(
// result.A-B = i.A x scale.A
" mul %A[i], %A[scale] \n\t"
// save results...
// basic idea:
//" mov %A[result], r0 \n\t"
//" mov %B[result], r1 \n\t"
// which can be written as...
" movw %A[result], r0 \n\t"
// We actually don't need to do anything with r0,
// as result.A is never used again here, so we
// could just move the high byte, but movw is
// one clock cycle, just like mov, so might as
// well, in case we want to use this code for
// a generic 16x16 multiply somewhere.
: [result] "=r" (result)
: [i] "r" (i),
[scale] "r" (scale)
: "r0", "r1"
);
asm volatile(
// result.C-D = i.B x scale.B
" mul %B[i], %B[scale] \n\t"
//" mov %C[result], r0 \n\t"
//" mov %D[result], r1 \n\t"
" movw %C[result], r0 \n\t"
: [result] "+r" (result)
: [i] "r" (i),
[scale] "r" (scale)
: "r0", "r1"
);
const uint8_t zero = 0;
asm volatile(
// result.B-D += i.B x scale.A
" mul %B[i], %A[scale] \n\t"
" add %B[result], r0 \n\t"
" adc %C[result], r1 \n\t"
" adc %D[result], %[zero] \n\t"
// result.B-D += i.A x scale.B
" mul %A[i], %B[scale] \n\t"
" add %B[result], r0 \n\t"
" adc %C[result], r1 \n\t"
" adc %D[result], %[zero] \n\t"
// cleanup r1
" clr r1 \n\t"
: [result] "+r" (result)
: [i] "r" (i),
[scale] "r" (scale),
[zero] "r" (zero)
: "r0", "r1"
);
result = result >> 16;
return result;
#endif
#else
#error "No implementation for scale16 available."
#endif
}
///@}
///@defgroup Dimming Dimming and brightening functions
///
/// Dimming and brightening functions
///
/// The eye does not respond in a linear way to light.
/// High speed PWM'd LEDs at 50% duty cycle appear far
/// brighter then the 'half as bright' you might expect.
///
/// If you want your midpoint brightness leve (128) to
/// appear half as bright as 'full' brightness (255), you
/// have to apply a 'dimming function'.
///@{
/// Adjust a scaling value for dimming
LIB8STATIC uint8_t dim8_raw( uint8_t x)
{
return scale8( x, x);
}
/// Adjust a scaling value for dimming for video (value will never go below 1)
LIB8STATIC uint8_t dim8_video( uint8_t x)
{
return scale8_video( x, x);
}
/// Linear version of the dimming function that halves for values < 128
LIB8STATIC uint8_t dim8_lin( uint8_t x )
{
if( x & 0x80 ) {
x = scale8( x, x);
} else {
x += 1;
x /= 2;
}
return x;
}
/// inverse of the dimming function, brighten a value
LIB8STATIC uint8_t brighten8_raw( uint8_t x)
{
uint8_t ix = 255 - x;
return 255 - scale8( ix, ix);
}
/// inverse of the dimming function, brighten a value
LIB8STATIC uint8_t brighten8_video( uint8_t x)
{
uint8_t ix = 255 - x;
return 255 - scale8_video( ix, ix);
}
/// inverse of the dimming function, brighten a value
LIB8STATIC uint8_t brighten8_lin( uint8_t x )
{
uint8_t ix = 255 - x;
if( ix & 0x80 ) {
ix = scale8( ix, ix);
} else {
ix += 1;
ix /= 2;
}
return 255 - ix;
}
///@}
#endif

View File

@@ -0,0 +1,259 @@
#ifndef __INC_LIB8TION_TRIG_H
#define __INC_LIB8TION_TRIG_H
///@ingroup lib8tion
///@defgroup Trig Fast trig functions
/// Fast 8 and 16-bit approximations of sin(x) and cos(x).
/// Don't use these approximations for calculating the
/// trajectory of a rocket to Mars, but they're great
/// for art projects and LED displays.
///
/// On Arduino/AVR, the 16-bit approximation is more than
/// 10X faster than floating point sin(x) and cos(x), while
/// the 8-bit approximation is more than 20X faster.
///@{
#if defined(__AVR__)
#define sin16 sin16_avr
#else
#define sin16 sin16_C
#endif
/// Fast 16-bit approximation of sin(x). This approximation never varies more than
/// 0.69% from the floating point value you'd get by doing
///
/// float s = sin(x) * 32767.0;
///
/// @param theta input angle from 0-65535
/// @returns sin of theta, value between -32767 to 32767.
LIB8STATIC int16_t sin16_avr( uint16_t theta )
{
static const uint8_t data[] =
{ 0, 0, 49, 0, 6393%256, 6393/256, 48, 0,
12539%256, 12539/256, 44, 0, 18204%256, 18204/256, 38, 0,
23170%256, 23170/256, 31, 0, 27245%256, 27245/256, 23, 0,
30273%256, 30273/256, 14, 0, 32137%256, 32137/256, 4 /*,0*/ };
uint16_t offset = (theta & 0x3FFF);
// AVR doesn't have a multi-bit shift instruction,
// so if we say "offset >>= 3", gcc makes a tiny loop.
// Inserting empty volatile statements between each
// bit shift forces gcc to unroll the loop.
offset >>= 1; // 0..8191
asm volatile("");
offset >>= 1; // 0..4095
asm volatile("");
offset >>= 1; // 0..2047
if( theta & 0x4000 ) offset = 2047 - offset;
uint8_t sectionX4;
sectionX4 = offset / 256;
sectionX4 *= 4;
uint8_t m;
union {
uint16_t b;
struct {
uint8_t blo;
uint8_t bhi;
};
} u;
//in effect u.b = blo + (256 * bhi);
u.blo = data[ sectionX4 ];
u.bhi = data[ sectionX4 + 1];
m = data[ sectionX4 + 2];
uint8_t secoffset8 = (uint8_t)(offset) / 2;
uint16_t mx = m * secoffset8;
int16_t y = mx + u.b;
if( theta & 0x8000 ) y = -y;
return y;
}
/// Fast 16-bit approximation of sin(x). This approximation never varies more than
/// 0.69% from the floating point value you'd get by doing
///
/// float s = sin(x) * 32767.0;
///
/// @param theta input angle from 0-65535
/// @returns sin of theta, value between -32767 to 32767.
LIB8STATIC int16_t sin16_C( uint16_t theta )
{
static const uint16_t base[] =
{ 0, 6393, 12539, 18204, 23170, 27245, 30273, 32137 };
static const uint8_t slope[] =
{ 49, 48, 44, 38, 31, 23, 14, 4 };
uint16_t offset = (theta & 0x3FFF) >> 3; // 0..2047
if( theta & 0x4000 ) offset = 2047 - offset;
uint8_t section = offset / 256; // 0..7
uint16_t b = base[section];
uint8_t m = slope[section];
uint8_t secoffset8 = (uint8_t)(offset) / 2;
uint16_t mx = m * secoffset8;
int16_t y = mx + b;
if( theta & 0x8000 ) y = -y;
return y;
}
/// Fast 16-bit approximation of cos(x). This approximation never varies more than
/// 0.69% from the floating point value you'd get by doing
///
/// float s = cos(x) * 32767.0;
///
/// @param theta input angle from 0-65535
/// @returns sin of theta, value between -32767 to 32767.
LIB8STATIC int16_t cos16( uint16_t theta)
{
return sin16( theta + 16384);
}
///////////////////////////////////////////////////////////////////////
// sin8 & cos8
// Fast 8-bit approximations of sin(x) & cos(x).
// Input angle is an unsigned int from 0-255.
// Output is an unsigned int from 0 to 255.
//
// This approximation can vary to to 2%
// from the floating point value you'd get by doing
// float s = (sin( x ) * 128.0) + 128;
//
// Don't use this approximation for calculating the
// "real" trigonometric calculations, but it's great
// for art projects and LED displays.
//
// On Arduino/AVR, this approximation is more than
// 20X faster than floating point sin(x) and cos(x)
#if defined(__AVR__) && !defined(LIB8_ATTINY)
#define sin8 sin8_avr
#else
#define sin8 sin8_C
#endif
const uint8_t b_m16_interleave[] = { 0, 49, 49, 41, 90, 27, 117, 10 };
/// Fast 8-bit approximation of sin(x). This approximation never varies more than
/// 2% from the floating point value you'd get by doing
///
/// float s = (sin(x) * 128.0) + 128;
///
/// @param theta input angle from 0-255
/// @returns sin of theta, value between 0 and 255
LIB8STATIC uint8_t sin8_avr( uint8_t theta)
{
uint8_t offset = theta;
asm volatile(
"sbrc %[theta],6 \n\t"
"com %[offset] \n\t"
: [theta] "+r" (theta), [offset] "+r" (offset)
);
offset &= 0x3F; // 0..63
uint8_t secoffset = offset & 0x0F; // 0..15
if( theta & 0x40) ++secoffset;
uint8_t m16; uint8_t b;
uint8_t section = offset >> 4; // 0..3
uint8_t s2 = section * 2;
const uint8_t* p = b_m16_interleave;
p += s2;
b = *p;
++p;
m16 = *p;
uint8_t mx;
uint8_t xr1;
asm volatile(
"mul %[m16],%[secoffset] \n\t"
"mov %[mx],r0 \n\t"
"mov %[xr1],r1 \n\t"
"eor r1, r1 \n\t"
"swap %[mx] \n\t"
"andi %[mx],0x0F \n\t"
"swap %[xr1] \n\t"
"andi %[xr1], 0xF0 \n\t"
"or %[mx], %[xr1] \n\t"
: [mx] "=d" (mx), [xr1] "=d" (xr1)
: [m16] "d" (m16), [secoffset] "d" (secoffset)
);
int8_t y = mx + b;
if( theta & 0x80 ) y = -y;
y += 128;
return y;
}
/// Fast 8-bit approximation of sin(x). This approximation never varies more than
/// 2% from the floating point value you'd get by doing
///
/// float s = (sin(x) * 128.0) + 128;
///
/// @param theta input angle from 0-255
/// @returns sin of theta, value between 0 and 255
LIB8STATIC uint8_t sin8_C( uint8_t theta)
{
uint8_t offset = theta;
if( theta & 0x40 ) {
offset = (uint8_t)255 - offset;
}
offset &= 0x3F; // 0..63
uint8_t secoffset = offset & 0x0F; // 0..15
if( theta & 0x40) ++secoffset;
uint8_t section = offset >> 4; // 0..3
uint8_t s2 = section * 2;
const uint8_t* p = b_m16_interleave;
p += s2;
uint8_t b = *p;
++p;
uint8_t m16 = *p;
uint8_t mx = (m16 * secoffset) >> 4;
int8_t y = mx + b;
if( theta & 0x80 ) y = -y;
y += 128;
return y;
}
/// Fast 8-bit approximation of cos(x). This approximation never varies more than
/// 2% from the floating point value you'd get by doing
///
/// float s = (cos(x) * 128.0) + 128;
///
/// @param theta input angle from 0-255
/// @returns sin of theta, value between 0 and 255
LIB8STATIC uint8_t cos8( uint8_t theta)
{
return sin8( theta + 64);
}
///@}
#endif

View File

@@ -0,0 +1,810 @@
#define FASTLED_INTERNAL
#include "FastLED.h"
#include <string.h>
FASTLED_NAMESPACE_BEGIN
#define P(x) FL_PGM_READ_BYTE_NEAR(p + x)
FL_PROGMEM static uint8_t const p[] = {
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225,
140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 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};
#if FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW == 1
#define AVG15(U,V) (((U)+(V)) >> 1)
#else
// See if we should use the inlined avg15 for AVR with MUL instruction
#if defined(__AVR__) && (LIB8_ATTINY == 0)
#define AVG15(U,V) (avg15_inline_avr_mul((U),(V)))
// inlined copy of avg15 for AVR with MUL instruction; cloned from math8.h
// Forcing this inline in the 3-D 16bit noise produces a 12% speedup overall,
// at a cost of just +8 bytes of net code size.
static int16_t inline __attribute__((always_inline)) avg15_inline_avr_mul( int16_t i, int16_t j)
{
asm volatile(
/* first divide j by 2, throwing away lowest bit */
"asr %B[j] \n\t"
"ror %A[j] \n\t"
/* now divide i by 2, with lowest bit going into C */
"asr %B[i] \n\t"
"ror %A[i] \n\t"
/* add j + C to i */
"adc %A[i], %A[j] \n\t"
"adc %B[i], %B[j] \n\t"
: [i] "+a" (i)
: [j] "a" (j) );
return i;
}
#else
#define AVG15(U,V) (avg15((U),(V)))
#endif
#endif
// See fastled_config.h for notes on this;
// "#define FASTLED_NOISE_FIXED 1" is the correct value
#if FASTLED_NOISE_FIXED == 0
#define EASE8(x) (FADE(x) )
#define EASE16(x) (FADE(x) )
#else
#define EASE8(x) (ease8InOutQuad(x) )
#define EASE16(x) (ease16InOutQuad(x))
#endif
//
// #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 inline __attribute__((always_inline)) grad16(uint8_t hash, int16_t x, int16_t y, int16_t z) {
#if 0
switch(hash & 0xF) {
case 0: return (( x) + ( y))>>1;
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 AVG15(u,v);
#endif
}
static int16_t inline __attribute__((always_inline)) grad16(uint8_t hash, int16_t x, int16_t y) {
hash = hash & 7;
int16_t u,v;
if(hash < 4) { u = x; v = y; } else { u = y; v = x; }
if(hash&1) { u = -u; }
if(hash&2) { v = -v; }
return AVG15(u,v);
}
static int16_t inline __attribute__((always_inline)) grad16(uint8_t hash, int16_t x) {
hash = hash & 15;
int16_t u,v;
if(hash > 8) { u=x;v=x; }
else if(hash < 4) { u=x;v=1; }
else { u=1;v=x; }
if(hash&1) { u = -u; }
if(hash&2) { v = -v; }
return AVG15(u,v);
}
// selectBasedOnHashBit performs this:
// result = (hash & (1<<bitnumber)) ? a : b
// but with an AVR asm version that's smaller and quicker than C
// (and probably not worth including in lib8tion)
static int8_t inline __attribute__((always_inline)) selectBasedOnHashBit(uint8_t hash, uint8_t bitnumber, int8_t a, int8_t b) {
int8_t result;
#if !defined(__AVR__)
result = (hash & (1<<bitnumber)) ? a : b;
#else
asm volatile(
"mov %[result],%[a] \n\t"
"sbrs %[hash],%[bitnumber] \n\t"
"mov %[result],%[b] \n\t"
: [result] "=r" (result)
: [hash] "r" (hash),
[bitnumber] "M" (bitnumber),
[a] "r" (a),
[b] "r" (b)
);
#endif
return result;
}
static int8_t inline __attribute__((always_inline)) grad8(uint8_t hash, int8_t x, int8_t y, int8_t z) {
#if 0
switch(hash & 0xF) {
case 0: return (( x) + ( y))>>1;
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, v;
//u = (hash&8)?y:x;
u = selectBasedOnHashBit( hash, 3, y, x);
#if 1
v = hash<4?y:hash==12||hash==14?x:z;
#else
// Verbose version for analysis; generates idenitical code.
if( hash < 4) { // 00 01 02 03
v = y;
} else {
if( hash==12 || hash==14) { // 0C 0E
v = x;
} else {
v = z; // 04 05 06 07 08 09 0A 0B 0D 0F
}
}
#endif
if(hash&1) { u = -u; }
if(hash&2) { v = -v; }
return avg7(u,v);
#endif
}
static int8_t inline __attribute__((always_inline)) grad8(uint8_t hash, int8_t x, int8_t y)
{
// since the tests below can be done bit-wise on the bottom
// three bits, there's no need to mask off the higher bits
// hash = hash & 7;
int8_t u,v;
if( hash & 4) {
u = y; v = x;
} else {
u = x; v = y;
}
if(hash&1) { u = -u; }
if(hash&2) { v = -v; }
return avg7(u,v);
}
static int8_t inline __attribute__((always_inline)) grad8(uint8_t hash, int8_t x)
{
// since the tests below can be done bit-wise on the bottom
// four bits, there's no need to mask off the higher bits
// hash = hash & 15;
int8_t u,v;
if(hash & 8) {
u=x; v=x;
} else {
if(hash & 4) {
u=1; v=x;
} else {
u=x; v=1;
}
}
if(hash&1) { u = -u; }
if(hash&2) { v = -v; }
return avg7(u,v);
}
#ifdef FADE_12
uint16_t logfade12(uint16_t val) {
return scale16(val,val)>>4;
}
static int16_t inline __attribute__((always_inline)) lerp15by12( int16_t a, int16_t b, fract16 frac)
{
//if(1) return (lerp(frac,a,b));
int16_t result;
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 inline __attribute__((always_inline)) lerp7by8( int8_t a, int8_t b, fract8 frac)
{
// int8_t delta = b - a;
// int16_t prod = (uint16_t)delta * (uint16_t)frac;
// 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 = EASE16(u); v = EASE16(v); w = EASE16(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;
// pan = (ans * 220L) >> 7. That's the same as:
// pan = (ans * 440L) >> 8. And this way avoids a 7X four-byte shift-loop on AVR.
// Identical math, except for the highest bit, which we don't care about anyway,
// since we're returning the 'middle' 16 out of a 32-bit value anyway.
pan *= 440L;
return (pan>>8);
// // 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 = EASE16(u); v = EASE16(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;
// pan = (ans * 242L) >> 7. That's the same as:
// pan = (ans * 484L) >> 8. And this way avoids a 7X four-byte shift-loop on AVR.
// Identical math, except for the highest bit, which we don't care about anyway,
// since we're returning the 'middle' 16 out of a 32-bit value anyway.
pan *= 484L;
return (pan>>8);
// 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 = EASE16(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 = ((uint8_t)(x)>>1) & 0x7F;
int8_t yy = ((uint8_t)(y)>>1) & 0x7F;
int8_t zz = ((uint8_t)(z)>>1) & 0x7F;
uint8_t N = 0x80;
u = EASE8(u); v = EASE8(v); w = EASE8(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 n = inoise8_raw( x, y, z); // -64..+64
n+= 64; // 0..128
uint8_t ans = qadd8( n, n); // 0..255
return ans;
}
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 = ((uint8_t)(x)>>1) & 0x7F;
int8_t yy = ((uint8_t)(y)>>1) & 0x7F;
uint8_t N = 0x80;
u = EASE8(u); v = EASE8(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 n = inoise8_raw( x, y); // -64..+64
n+= 64; // 0..128
uint8_t ans = qadd8( n, n); // 0..255
return ans;
}
// output range = -64 .. +64
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 = ((uint8_t)(x)>>1) & 0x7F;
uint8_t N = 0x80;
u = EASE8( u);
int8_t ans = lerp7by8(grad8(P(AA), xx), grad8(P(BA), xx - N), u);
return ans;
}
uint8_t inoise8(uint16_t x) {
int8_t n = inoise8_raw(x); //-64..+64
n += 64; // 0..128
uint8_t ans = qadd8(n,n); // 0..255
return ans;
}
// 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), 128, 1, x, scalex, y, scaley, time);
}
void fill_raw_2dnoise16(uint16_t *pData, int width, int height, uint8_t octaves, q88 freq88, fract16 amplitude, int skip, uint32_t x, int scalex, uint32_t y, int scaley, uint32_t time) {
if(octaves > 1) {
fill_raw_2dnoise16(pData, width, height, octaves-1, freq88, amplitude, skip, 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 hue_shift) {
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] + hue_shift,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;
}
}
}
}
FASTLED_NAMESPACE_END

View File

@@ -0,0 +1,97 @@
#ifndef __INC_NOISE_H
#define __INC_NOISE_H
#include "FastLED.h"
FASTLED_NAMESPACE_BEGIN
///@file noise.h
/// Noise functions provided by the library.
///@defgroup Noise Noise functions
///Simplex noise function definitions
///@{
/// @name scaled 16 bit noise functions
///@{
/// 16 bit, fixed point implementation of perlin's Simplex Noise. Coordinates are
/// 16.16 fixed point values, 32 bit integers with integral coordinates in the high 16
/// bits and fractional in the low 16 bits, and the function takes 1d, 2d, and 3d coordinate
/// values. These functions are scaled to return 0-65535
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);
///@}
/// @name raw 16 bit noise functions
//@{
/// 16 bit raw versions of the noise functions. These values are not scaled/altered and have
/// output values roughly in the range (-18k,18k)
extern int16_t inoise16_raw(uint32_t x, uint32_t y, uint32_t z);
extern int16_t inoise16_raw(uint32_t x, uint32_t y);
extern int16_t inoise16_raw(uint32_t x);
///@}
/// @name 8 bit scaled noise functions
///@{
/// 8 bit, fixed point implementation of perlin's Simplex Noise. Coordinates are
/// 8.8 fixed point values, 16 bit integers with integral coordinates in the high 8
/// bits and fractional in the low 8 bits, and the function takes 1d, 2d, and 3d coordinate
/// values. These functions are scaled to return 0-255
extern uint8_t inoise8(uint16_t x, uint16_t y, uint16_t z);
extern uint8_t inoise8(uint16_t x, uint16_t y);
extern uint8_t inoise8(uint16_t x);
///@}
/// @name 8 bit raw noise functions
///@{
/// 8 bit raw versions of the noise functions. These values are not scaled/altered and have
/// output values roughly in the range (-70,70)
extern int8_t inoise8_raw(uint16_t x, uint16_t y, uint16_t z);
extern int8_t inoise8_raw(uint16_t x, uint16_t y);
extern int8_t inoise8_raw(uint16_t x);
///@}
///@name raw fill functions
///@{
/// Raw noise fill functions - fill into a 1d or 2d array of 8-bit values using either 8-bit noise or 16-bit noise
/// functions.
///@param pData the array of data to write into
///@param num_points the number of points of noise to compute
///@param octaves the number of octaves to use for noise
///@param x the x position in the noise field
///@param y the y position in the noise field for 2d functions
///@param scalex the scale (distance) between x points when filling in noise
///@param scaley the scale (distance) between y points when filling in noise
///@param time the time position for the noise field
void fill_raw_noise8(uint8_t *pData, uint8_t num_points, uint8_t octaves, uint16_t x, int scalex, uint16_t time);
void fill_raw_noise16into8(uint8_t *pData, uint8_t num_points, uint8_t octaves, uint32_t x, int scalex, uint32_t time);
void fill_raw_2dnoise8(uint8_t *pData, int width, int height, uint8_t octaves, uint16_t x, int scalex, uint16_t y, int scaley, uint16_t time);
void fill_raw_2dnoise16into8(uint8_t *pData, int width, int height, uint8_t octaves, uint32_t x, int scalex, uint32_t y, int scaley, uint32_t time);
void fill_raw_2dnoise16(uint16_t *pData, int width, int height, uint8_t octaves, q88 freq88, fract16 amplitude, int skip, uint32_t x, int scalex, uint32_t y, int scaley, uint32_t time);
void fill_raw_2dnoise16into8(uint8_t *pData, int width, int height, uint8_t octaves, q44 freq44, fract8 amplitude, int skip, uint32_t x, int scalex, uint32_t y, int scaley, uint32_t time);
///@}
///@name fill functions
///@{
/// fill functions to fill leds with values based on noise functions. These functions use the fill_raw_* functions as appropriate.
void fill_noise8(CRGB *leds, int num_leds,
uint8_t octaves, uint16_t x, int scale,
uint8_t hue_octaves, uint16_t hue_x, int hue_scale,
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, uint8_t hue_shift=0);
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);
FASTLED_NAMESPACE_END
///@}
#endif

View File

@@ -0,0 +1,306 @@
#ifndef __INC_PIXELSET_H
#define __INC_PIXELSET_H
#include "FastLED.h"
#ifndef abs
#include <stdlib.h>
#endif
///// Represents a set of CRGB led objects. Provides the [] array operator, and works like a normal array in that case.
///// This should be kept in sync with the set of functions provided by CRGB as well as functions in colorutils. Note
///// that a pixel set is a window into another set of led data, it is not its own set of led data.
template<class PIXEL_TYPE>
class CPixelView {
public:
const int8_t dir;
const int len;
PIXEL_TYPE * const leds;
PIXEL_TYPE * const end_pos;
public:
/// PixelSet copy constructor
inline CPixelView(const CPixelView & other) : dir(other.dir), len(other.len), leds(other.leds), end_pos(other.end_pos) {}
/// pixelset constructor for a pixel set starting at the given PIXEL_TYPE* and going for _len leds. Note that the length
/// can be backwards, creating a PixelSet that walks backwards over the data
/// @param leds point to the raw led data
/// @param len how many leds in this set
inline CPixelView(PIXEL_TYPE *_leds, int _len) : dir(_len < 0 ? -1 : 1), len(_len), leds(_leds), end_pos(_leds + _len) {}
/// PixelSet constructor for the given set of leds, with start and end boundaries. Note that start can be after
/// end, resulting in a set that will iterate backwards
/// @param leds point to the raw led data
/// @param start the start index of the leds for this array
/// @param end the end index of the leds for this array
inline CPixelView(PIXEL_TYPE *_leds, int _start, int _end) : dir(((_end-_start)<0) ? -1 : 1), len((_end - _start) + dir), leds(_leds + _start), end_pos(_leds + _start + len) {}
/// Get the size of this set
/// @return the size of the set
int size() { return abs(len); }
/// Whether or not this set goes backwards
/// @return whether or not the set is backwards
bool reversed() { return len < 0; }
/// do these sets point to the same thing (note, this is different from the contents of the set being the same)
bool operator==(const CPixelView & rhs) const { return leds == rhs.leds && len == rhs.len && dir == rhs.dir; }
/// do these sets point to the different things (note, this is different from the contents of the set being the same)
bool operator!=(const CPixelView & rhs) const { return leds != rhs.leds || len != rhs.len || dir != rhs.dir; }
/// access a single element in this set, just like an array operator
inline PIXEL_TYPE & operator[](int x) const { if(dir & 0x80) { return leds[-x]; } else { return leds[x]; } }
/// Access an inclusive subset of the leds in this set. Note that start can be greater than end, which will
/// result in a reverse ordering for many functions (useful for mirroring)
/// @param start the first element from this set for the new subset
/// @param end the last element for the new subset
inline CPixelView operator()(int start, int end) { return CPixelView(leds, start, end); }
/// Access an inclusive subset of the leds in this set, starting from the first.
/// @param end the last element for the new subset
/// Not sure i want this? inline CPixelView operator()(int end) { return CPixelView(leds, 0, end); }
/// Return the reverse ordering of this set
inline CPixelView operator-() { return CPixelView(leds, len - dir, 0); }
/// Return a pointer to the first element in this set
inline operator PIXEL_TYPE* () const { return leds; }
/// Assign the passed in color to all elements in this set
/// @param color the new color for the elements in the set
inline CPixelView & operator=(const PIXEL_TYPE & color) {
for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) = color; }
return *this;
}
void dump() const {
/**
Serial.print("len: "); Serial.print(len); Serial.print(", dir:"); Serial.print((int)dir);
Serial.print(", range:"); Serial.print((uint32_t)leds); Serial.print("-"); Serial.print((uint32_t)end_pos);
Serial.print(", diff:"); Serial.print((int32_t)(end_pos - leds));
Serial.println("");
**/
}
/// Copy the contents of the passed in set to our set. Note if one set is smaller than the other, only the
/// smallest number of items will be copied over.
inline CPixelView & operator=(const CPixelView & rhs) {
for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) {
(*pixel) = (*rhspixel);
}
return *this;
}
/// @name modification/scaling operators
//@{
/// Add the passed in value to r,g, b for all the pixels in this set
inline CPixelView & addToRGB(uint8_t inc) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) += inc; } return *this; }
/// Add every pixel in the other set to this set
inline CPixelView & operator+=(CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) += (*rhspixel); } return *this; }
/// Subtract the passed in value from r,g,b for all pixels in this set
inline CPixelView & subFromRGB(uint8_t inc) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) -= inc; } return *this; }
/// Subtract every pixel in the other set from this set
inline CPixelView & operator-=(CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) -= (*rhspixel); } return *this; }
/// Increment every pixel value in this set
inline CPixelView & operator++() { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)++; } return *this; }
/// Increment every pixel value in this set
inline CPixelView & operator++(int DUMMY_ARG) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)++; } return *this; }
/// Decrement every pixel value in this set
inline CPixelView & operator--() { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)--; } return *this; }
/// Decrement every pixel value in this set
inline CPixelView & operator--(int DUMMY_ARG) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)--; } return *this; }
/// Divide every led by the given value
inline CPixelView & operator/=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) /= d; } return *this; }
/// Shift every led in this set right by the given number of bits
inline CPixelView & operator>>=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) >>= d; } return *this; }
/// Multiply every led in this set by the given value
inline CPixelView & operator*=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) *= d; } return *this; }
/// Scale every led by the given scale
inline CPixelView & nscale8_video(uint8_t scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8_video(scaledown); } return *this;}
/// Scale down every led by the given scale
inline CPixelView & operator%=(uint8_t scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8_video(scaledown); } return *this; }
/// Fade every led down by the given scale
inline CPixelView & fadeLightBy(uint8_t fadefactor) { return nscale8_video(255 - fadefactor); }
/// Scale every led by the given scale
inline CPixelView & nscale8(uint8_t scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8(scaledown); } return *this; }
/// Scale every led by the given scale
inline CPixelView & nscale8(PIXEL_TYPE & scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8(scaledown); } return *this; }
/// Scale every led in this set by every led in the other set
inline CPixelView & nscale8(CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel).nscale8((*rhspixel)); } return *this; }
/// Fade every led down by the given scale
inline CPixelView & fadeToBlackBy(uint8_t fade) { return nscale8(255 - fade); }
/// Apply the PIXEL_TYPE |= operator to every pixel in this set with the given PIXEL_TYPE value (bringing each channel to the higher of the two values)
inline CPixelView & operator|=(const PIXEL_TYPE & rhs) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) |= rhs; } return *this; }
/// Apply the PIXEL_TYPE |= operator to every pixel in this set with every pixel in the passed in set
inline CPixelView & operator|=(const CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) |= (*rhspixel); } return *this; }
/// Apply the PIXEL_TYPE |= operator to every pixel in this set
inline CPixelView & operator|=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) |= d; } return *this; }
/// Apply the PIXEL_TYPE &= operator to every pixel in this set with the given PIXEL_TYPE value (bringing each channel down to the lower of the two values)
inline CPixelView & operator&=(const PIXEL_TYPE & rhs) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) &= rhs; } return *this; }
/// Apply the PIXEL_TYPE &= operator to every pixel in this set with every pixel in the passed in set
inline CPixelView & operator&=(const CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) &= (*rhspixel); } return *this; }
/// APply the PIXEL_TYPE &= operator to every pixel in this set with the passed in value
inline CPixelView & operator&=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) &= d; } return *this; }
//@}
/// Returns whether or not any leds in this set are non-zero
inline operator bool() { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { if((*pixel)) return true; } return false; }
// Color util functions
inline CPixelView & fill_solid(const PIXEL_TYPE & color) { *this = color; return *this; }
inline CPixelView & fill_solid(const CHSV & color) { if(dir>0) { *this = color; return *this; } }
inline CPixelView & fill_rainbow(uint8_t initialhue, uint8_t deltahue=5) {
if(dir >= 0) {
::fill_rainbow(leds,len,initialhue,deltahue);
} else {
::fill_rainbow(leds+len+1,-len,initialhue,deltahue);
}
return *this;
}
inline CPixelView & fill_gradient(const CHSV & startcolor, const CHSV & endcolor, TGradientDirectionCode directionCode = SHORTEST_HUES) {
if(dir >= 0) {
::fill_gradient(leds,len,startcolor, endcolor, directionCode);
} else {
::fill_gradient(leds + len + 1, (-len), endcolor, startcolor, directionCode);
}
return *this;
}
inline CPixelView & fill_gradient(const CHSV & c1, const CHSV & c2, const CHSV & c3, TGradientDirectionCode directionCode = SHORTEST_HUES) {
if(dir >= 0) {
::fill_gradient(leds, len, c1, c2, c3, directionCode);
} else {
::fill_gradient(leds + len + 1, -len, c3, c2, c1, directionCode);
}
return *this;
}
inline CPixelView & fill_gradient(const CHSV & c1, const CHSV & c2, const CHSV & c3, const CHSV & c4, TGradientDirectionCode directionCode = SHORTEST_HUES) {
if(dir >= 0) {
::fill_gradient(leds, len, c1, c2, c3, c4, directionCode);
} else {
::fill_gradient(leds + len + 1, -len, c4, c3, c2, c1, directionCode);
}
return *this;
}
inline CPixelView & fill_gradient_RGB(const PIXEL_TYPE & startcolor, const PIXEL_TYPE & endcolor, TGradientDirectionCode directionCode = SHORTEST_HUES) {
if(dir >= 0) {
::fill_gradient_RGB(leds,len,startcolor, endcolor);
} else {
::fill_gradient_RGB(leds + len + 1, (-len), endcolor, startcolor);
}
return *this;
}
inline CPixelView & fill_gradient_RGB(const PIXEL_TYPE & c1, const PIXEL_TYPE & c2, const PIXEL_TYPE & c3) {
if(dir >= 0) {
::fill_gradient_RGB(leds, len, c1, c2, c3);
} else {
::fill_gradient_RGB(leds + len + 1, -len, c3, c2, c1);
}
return *this;
}
inline CPixelView & fill_gradient_RGB(const PIXEL_TYPE & c1, const PIXEL_TYPE & c2, const PIXEL_TYPE & c3, const PIXEL_TYPE & c4) {
if(dir >= 0) {
::fill_gradient_RGB(leds, len, c1, c2, c3, c4);
} else {
::fill_gradient_RGB(leds + len + 1, -len, c4, c3, c2, c1);
}
return *this;
}
inline CPixelView & nblend(const PIXEL_TYPE & overlay, fract8 amountOfOverlay) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { ::nblend((*pixel), overlay, amountOfOverlay); } return *this; }
inline CPixelView & nblend(const CPixelView & rhs, fract8 amountOfOverlay) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { ::nblend((*pixel), (*rhspixel), amountOfOverlay); } return *this; }
// Note: only bringing in a 1d blur, not sure 2d blur makes sense when looking at sub arrays
inline CPixelView & blur1d(fract8 blur_amount) {
if(dir >= 0) {
::blur1d(leds, len, blur_amount);
} else {
::blur1d(leds + len + 1, -len, blur_amount);
}
return *this;
}
inline CPixelView & napplyGamma_video(float gamma) {
if(dir >= 0) {
::napplyGamma_video(leds, len, gamma);
} else {
::napplyGamma_video(leds + len + 1, -len, gamma);
}
return *this;
}
inline CPixelView & napplyGamma_video(float gammaR, float gammaG, float gammaB) {
if(dir >= 0) {
::napplyGamma_video(leds, len, gammaR, gammaG, gammaB);
} else {
::napplyGamma_video(leds + len + 1, -len, gammaR, gammaG, gammaB);
}
return *this;
}
// TODO: Make this a fully specified/proper iterator
template <class T>
class pixelset_iterator_base {
T * leds;
const int8_t dir;
public:
__attribute__((always_inline)) inline pixelset_iterator_base(const pixelset_iterator_base & rhs) : leds(rhs.leds), dir(rhs.dir) {}
__attribute__((always_inline)) inline pixelset_iterator_base(T * _leds, const char _dir) : leds(_leds), dir(_dir) {}
__attribute__((always_inline)) inline pixelset_iterator_base& operator++() { leds += dir; return *this; }
__attribute__((always_inline)) inline pixelset_iterator_base operator++(int) { pixelset_iterator_base tmp(*this); leds += dir; return tmp; }
__attribute__((always_inline)) inline bool operator==(pixelset_iterator_base & other) const { return leds == other.leds; } // && set==other.set; }
__attribute__((always_inline)) inline bool operator!=(pixelset_iterator_base & other) const { return leds != other.leds; } // || set != other.set; }
__attribute__((always_inline)) inline PIXEL_TYPE& operator*() const { return *leds; }
};
typedef pixelset_iterator_base<PIXEL_TYPE> iterator;
typedef pixelset_iterator_base<const PIXEL_TYPE> const_iterator;
iterator begin() { return iterator(leds, dir); }
iterator end() { return iterator(end_pos, dir); }
iterator begin() const { return iterator(leds, dir); }
iterator end() const { return iterator(end_pos, dir); }
const_iterator cbegin() const { return const_iterator(leds, dir); }
const_iterator cend() const { return const_iterator(end_pos, dir); }
};
typedef CPixelView<CRGB> CRGBSet;
__attribute__((always_inline))
inline CRGB *operator+(const CRGBSet & pixels, int offset) { return (CRGB*)pixels + offset; }
template<int SIZE>
class CRGBArray : public CPixelView<CRGB> {
CRGB rawleds[SIZE];
public:
CRGBArray() : CPixelView<CRGB>(rawleds, SIZE) {}
using CPixelView::operator=;
};
#endif

View File

@@ -0,0 +1,856 @@
#ifndef __INC_PIXELS_H
#define __INC_PIXELS_H
#include "FastLED.h"
#include <stdint.h>
#include "lib8tion.h"
#include "color.h"
FASTLED_NAMESPACE_BEGIN
struct CRGB;
struct CHSV;
///@defgroup Pixeltypes CHSV and CRGB type definitions
///@{
/// Forward declaration of hsv2rgb_rainbow here,
/// to avoid circular dependencies.
extern void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb);
/// Representation of an HSV pixel (hue, saturation, value (aka brightness)).
struct CHSV {
union {
struct {
union {
uint8_t hue;
uint8_t h; };
union {
uint8_t saturation;
uint8_t sat;
uint8_t s; };
union {
uint8_t value;
uint8_t val;
uint8_t v; };
};
uint8_t raw[3];
};
/// Array access operator to index into the chsv object
inline uint8_t& operator[] (uint8_t x) __attribute__((always_inline))
{
return raw[x];
}
/// Array access operator to index into the chsv object
inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline))
{
return raw[x];
}
/// default values are UNITIALIZED
inline CHSV() __attribute__((always_inline)) = default;
/// allow construction from H, S, V
inline CHSV( uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline))
: h(ih), s(is), v(iv)
{
}
/// allow copy construction
inline CHSV(const CHSV& rhs) __attribute__((always_inline)) = default;
inline CHSV& operator= (const CHSV& rhs) __attribute__((always_inline)) = default;
inline CHSV& setHSV(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline))
{
h = ih;
s = is;
v = iv;
return *this;
}
};
/// Pre-defined hue values for HSV objects
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;
/// Representation of an RGB pixel (Red, Green, Blue)
struct CRGB {
union {
struct {
union {
uint8_t r;
uint8_t red;
};
union {
uint8_t g;
uint8_t green;
};
union {
uint8_t b;
uint8_t blue;
};
};
uint8_t raw[3];
};
/// Array access operator to index into the crgb object
inline uint8_t& operator[] (uint8_t x) __attribute__((always_inline))
{
return raw[x];
}
/// Array access operator to index into the crgb object
inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline))
{
return raw[x];
}
// default values are UNINITIALIZED
inline CRGB() __attribute__((always_inline)) = default;
/// 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)
{
}
/// allow construction from a LEDColorCorrection enum
inline CRGB( LEDColorCorrection colorcode) __attribute__((always_inline))
: r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF)
{
}
/// allow construction from a ColorTemperature enum
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)) = default;
/// allow construction from HSV color
inline CRGB(const CHSV& rhs) __attribute__((always_inline))
{
hsv2rgb_rainbow( rhs, *this);
}
/// allow assignment from one RGB struct to another
inline CRGB& operator= (const CRGB& rhs) __attribute__((always_inline)) = default;
/// allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code
inline CRGB& operator= (const uint32_t colorcode) __attribute__((always_inline))
{
r = (colorcode >> 16) & 0xFF;
g = (colorcode >> 8) & 0xFF;
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))
{
r = nr;
g = ng;
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))
{
r = (colorcode >> 16) & 0xFF;
g = (colorcode >> 8) & 0xFF;
b = (colorcode >> 0) & 0xFF;
return *this;
}
/// add one RGB to another, saturating at 0xFF for each channel
inline CRGB& operator+= (const CRGB& rhs )
{
r = qadd8( r, rhs.r);
g = qadd8( g, rhs.g);
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
/// constant (e.g. CRGB::Red) and an 8-bit one (CRGB::Blue)
inline CRGB& addToRGB (uint8_t d )
{
r = qadd8( r, d);
g = qadd8( g, d);
b = qadd8( b, d);
return *this;
}
/// subtract one RGB from another, saturating at 0x00 for each channel
inline CRGB& operator-= (const CRGB& rhs )
{
r = qsub8( r, rhs.r);
g = qsub8( g, rhs.g);
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
/// constant (e.g. CRGB::Red) and an 8-bit one (CRGB::Blue)
inline CRGB& subtractFromRGB(uint8_t d )
{
r = qsub8( r, d);
g = qsub8( g, d);
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 ) __attribute__((always_inline))
{
CRGB retval(*this);
--(*this);
return retval;
}
/// add a constant of '1' from each channel, saturating at 0xFF
inline CRGB& operator++ () __attribute__((always_inline))
{
addToRGB(1);
return *this;
}
/// add a constant of '1' from each channel, saturating at 0xFF
inline CRGB operator++ (int ) __attribute__((always_inline))
{
CRGB retval(*this);
++(*this);
return retval;
}
/// divide each of the channels by a constant
inline CRGB& operator/= (uint8_t d )
{
r /= d;
g /= d;
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 )
{
r = qmul8( r, d);
g = qmul8( g, d);
b = qmul8( b, d);
return *this;
}
/// scale down a RGB to N 256ths of it's current brightness, using
/// 'video' dimming rules, which means that unless the scale factor is ZERO
/// each channel is guaranteed NOT to dim down to zero. If it's already
/// nonzero, it'll stay nonzero, even if that means the hue shifts a little
/// at low brightness levels.
inline CRGB& nscale8_video (uint8_t scaledown )
{
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 )
{
nscale8x3_video( r, g, b, scaledown);
return *this;
}
/// fadeLightBy is a synonym for nscale8_video( ..., 255-fadefactor)
inline CRGB& fadeLightBy (uint8_t fadefactor )
{
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.
inline CRGB& nscale8 (uint8_t scaledown )
{
nscale8x3( r, g, b, scaledown);
return *this;
}
/// scale down a RGB to N 256ths of it's current brightness, using
/// 'plain math' dimming rules, which means that if the low light levels
/// may dim all the way to 100% black.
inline CRGB& nscale8 (const CRGB & scaledown )
{
r = ::scale8(r, scaledown.r);
g = ::scale8(g, scaledown.g);
b = ::scale8(b, scaledown.b);
return *this;
}
/// return a CRGB object that is a scaled down version of this object
inline CRGB scale8 (const CRGB & scaledown ) const
{
CRGB out;
out.r = ::scale8(r, scaledown.r);
out.g = ::scale8(g, scaledown.g);
out.b = ::scale8(b, scaledown.b);
return out;
}
/// fadeToBlackBy is a synonym for nscale8( ..., 255-fadefactor)
inline CRGB& fadeToBlackBy (uint8_t fadefactor )
{
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 )
{
if( rhs.r > r) r = rhs.r;
if( rhs.g > g) g = rhs.g;
if( rhs.b > b) b = rhs.b;
return *this;
}
/// "or" operator brings each channel up to the higher of the two values
inline CRGB& operator|= (uint8_t d )
{
if( d > r) r = d;
if( d > g) g = d;
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 )
{
if( rhs.r < r) r = rhs.r;
if( rhs.g < g) g = rhs.g;
if( rhs.b < b) b = rhs.b;
return *this;
}
/// "and" operator brings each channel down to the lower of the two values
inline CRGB& operator&= (uint8_t d )
{
if( d < r) r = d;
if( d < g) g = d;
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- ()
{
CRGB retval;
retval.r = 255 - r;
retval.g = 255 - g;
retval.b = 255 - b;
return retval;
}
#if (defined SmartMatrix_h || defined SmartMatrix3_h)
operator rgb24() const {
rgb24 ret;
ret.red = r;
ret.green = g;
ret.blue = b;
return ret;
}
#endif
/// Get the 'luma' of a CRGB object - aka roughly how much light the
/// CRGB pixel is putting out (from 0 to 255).
inline uint8_t getLuma ( ) const {
//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;
}
/// Get the average of the R, G, and B values
inline uint8_t getAverageLight( ) const {
#if FASTLED_SCALE8_FIXED == 1
const uint8_t eightyfive = 85;
#else
const uint8_t eightyfive = 86;
#endif
uint8_t avg = scale8_LEAVING_R1_DIRTY( r, eightyfive) + \
scale8_LEAVING_R1_DIRTY( g, eightyfive) + \
scale8_LEAVING_R1_DIRTY( b, eightyfive);
cleanup_R1();
return avg;
}
/// maximize the brightness of this CRGB object
inline void maximizeBrightness( uint8_t limit = 255 ) {
uint8_t max = red;
if( green > max) max = green;
if( blue > max) max = blue;
// stop div/0 when color is black
if(max > 0) {
uint16_t factor = ((uint16_t)(limit) * 256) / max;
red = (red * factor) / 256;
green = (green * factor) / 256;
blue = (blue * factor) / 256;
}
}
/// return a new CRGB object after performing a linear interpolation between this object and the passed in object
inline CRGB lerp8( const CRGB& other, fract8 frac) const
{
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;
}
/// return a new CRGB object after performing a linear interpolation between this object and the passed in object
inline CRGB lerp16( const CRGB& other, fract16 frac) const
{
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;
}
/// getParity returns 0 or 1, depending on the
/// lowest bit of the sum of the color components.
inline uint8_t getParity()
{
uint8_t sum = r + g + b;
return (sum & 0x01);
}
/// setParity adjusts the color in the smallest
/// way possible so that the parity of the color
/// is now the desired value. This allows you to
/// 'hide' one bit of information in the color.
///
/// Ideally, we find one color channel which already
/// has data in it, and modify just that channel by one.
/// We don't want to light up a channel that's black
/// if we can avoid it, and if the pixel is 'grayscale',
/// (meaning that R==G==B), we modify all three channels
/// at once, to preserve the neutral hue.
///
/// There's no such thing as a free lunch; in many cases
/// this 'hidden bit' may actually be visible, but this
/// code makes reasonable efforts to hide it as much
/// as is reasonably possible.
///
/// Also, an effort is made to have make it such that
/// repeatedly setting the parity to different values
/// will not cause the color to 'drift'. Toggling
/// the parity twice should generally result in the
/// original color again.
///
inline void setParity( uint8_t parity)
{
uint8_t curparity = getParity();
if( parity == curparity) return;
if( parity ) {
// going 'up'
if( (b > 0) && (b < 255)) {
if( r == g && g == b) {
++r;
++g;
}
++b;
} else if( (r > 0) && (r < 255)) {
++r;
} else if( (g > 0) && (g < 255)) {
++g;
} else {
if( r == g && g == b) {
r ^= 0x01;
g ^= 0x01;
}
b ^= 0x01;
}
} else {
// going 'down'
if( b > 1) {
if( r == g && g == b) {
--r;
--g;
}
--b;
} else if( g > 1) {
--g;
} else if( r > 1) {
--r;
} else {
if( r == g && g == b) {
r ^= 0x01;
g ^= 0x01;
}
b ^= 0x01;
}
}
}
/// Predefined RGB colors
typedef enum {
AliceBlue=0xF0F8FF,
Amethyst=0x9966CC,
AntiqueWhite=0xFAEBD7,
Aqua=0x00FFFF,
Aquamarine=0x7FFFD4,
Azure=0xF0FFFF,
Beige=0xF5F5DC,
Bisque=0xFFE4C4,
Black=0x000000,
BlanchedAlmond=0xFFEBCD,
Blue=0x0000FF,
BlueViolet=0x8A2BE2,
Brown=0xA52A2A,
BurlyWood=0xDEB887,
CadetBlue=0x5F9EA0,
Chartreuse=0x7FFF00,
Chocolate=0xD2691E,
Coral=0xFF7F50,
CornflowerBlue=0x6495ED,
Cornsilk=0xFFF8DC,
Crimson=0xDC143C,
Cyan=0x00FFFF,
DarkBlue=0x00008B,
DarkCyan=0x008B8B,
DarkGoldenrod=0xB8860B,
DarkGray=0xA9A9A9,
DarkGrey=0xA9A9A9,
DarkGreen=0x006400,
DarkKhaki=0xBDB76B,
DarkMagenta=0x8B008B,
DarkOliveGreen=0x556B2F,
DarkOrange=0xFF8C00,
DarkOrchid=0x9932CC,
DarkRed=0x8B0000,
DarkSalmon=0xE9967A,
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,
ForestGreen=0x228B22,
Fuchsia=0xFF00FF,
Gainsboro=0xDCDCDC,
GhostWhite=0xF8F8FF,
Gold=0xFFD700,
Goldenrod=0xDAA520,
Gray=0x808080,
Grey=0x808080,
Green=0x008000,
GreenYellow=0xADFF2F,
Honeydew=0xF0FFF0,
HotPink=0xFF69B4,
IndianRed=0xCD5C5C,
Indigo=0x4B0082,
Ivory=0xFFFFF0,
Khaki=0xF0E68C,
Lavender=0xE6E6FA,
LavenderBlush=0xFFF0F5,
LawnGreen=0x7CFC00,
LemonChiffon=0xFFFACD,
LightBlue=0xADD8E6,
LightCoral=0xF08080,
LightCyan=0xE0FFFF,
LightGoldenrodYellow=0xFAFAD2,
LightGreen=0x90EE90,
LightGrey=0xD3D3D3,
LightPink=0xFFB6C1,
LightSalmon=0xFFA07A,
LightSeaGreen=0x20B2AA,
LightSkyBlue=0x87CEFA,
LightSlateGray=0x778899,
LightSlateGrey=0x778899,
LightSteelBlue=0xB0C4DE,
LightYellow=0xFFFFE0,
Lime=0x00FF00,
LimeGreen=0x32CD32,
Linen=0xFAF0E6,
Magenta=0xFF00FF,
Maroon=0x800000,
MediumAquamarine=0x66CDAA,
MediumBlue=0x0000CD,
MediumOrchid=0xBA55D3,
MediumPurple=0x9370DB,
MediumSeaGreen=0x3CB371,
MediumSlateBlue=0x7B68EE,
MediumSpringGreen=0x00FA9A,
MediumTurquoise=0x48D1CC,
MediumVioletRed=0xC71585,
MidnightBlue=0x191970,
MintCream=0xF5FFFA,
MistyRose=0xFFE4E1,
Moccasin=0xFFE4B5,
NavajoWhite=0xFFDEAD,
Navy=0x000080,
OldLace=0xFDF5E6,
Olive=0x808000,
OliveDrab=0x6B8E23,
Orange=0xFFA500,
OrangeRed=0xFF4500,
Orchid=0xDA70D6,
PaleGoldenrod=0xEEE8AA,
PaleGreen=0x98FB98,
PaleTurquoise=0xAFEEEE,
PaleVioletRed=0xDB7093,
PapayaWhip=0xFFEFD5,
PeachPuff=0xFFDAB9,
Peru=0xCD853F,
Pink=0xFFC0CB,
Plaid=0xCC5533,
Plum=0xDDA0DD,
PowderBlue=0xB0E0E6,
Purple=0x800080,
Red=0xFF0000,
RosyBrown=0xBC8F8F,
RoyalBlue=0x4169E1,
SaddleBrown=0x8B4513,
Salmon=0xFA8072,
SandyBrown=0xF4A460,
SeaGreen=0x2E8B57,
Seashell=0xFFF5EE,
Sienna=0xA0522D,
Silver=0xC0C0C0,
SkyBlue=0x87CEEB,
SlateBlue=0x6A5ACD,
SlateGray=0x708090,
SlateGrey=0x708090,
Snow=0xFFFAFA,
SpringGreen=0x00FF7F,
SteelBlue=0x4682B4,
Tan=0xD2B48C,
Teal=0x008080,
Thistle=0xD8BFD8,
Tomato=0xFF6347,
Turquoise=0x40E0D0,
Violet=0xEE82EE,
Wheat=0xF5DEB3,
White=0xFFFFFF,
WhiteSmoke=0xF5F5F5,
Yellow=0xFFFF00,
YellowGreen=0x9ACD32,
// LED RGB color that roughly approximates
// the color of incandescent fairy lights,
// assuming that you're using FastLED
// color correction on your LEDs (recommended).
FairyLight=0xFFE42D,
// If you are using no color correction, use this
FairyLightNCC=0xFF9D2A
} HTMLColorCode;
};
inline __attribute__((always_inline)) bool operator== (const CRGB& lhs, const CRGB& rhs)
{
return (lhs.r == rhs.r) && (lhs.g == rhs.g) && (lhs.b == rhs.b);
}
inline __attribute__((always_inline)) bool operator!= (const CRGB& lhs, const CRGB& rhs)
{
return !(lhs == rhs);
}
inline __attribute__((always_inline)) bool operator< (const CRGB& lhs, const CRGB& rhs)
{
uint16_t sl, sr;
sl = lhs.r + lhs.g + lhs.b;
sr = rhs.r + rhs.g + rhs.b;
return sl < sr;
}
inline __attribute__((always_inline)) bool operator> (const CRGB& lhs, const CRGB& rhs)
{
uint16_t sl, sr;
sl = lhs.r + lhs.g + lhs.b;
sr = rhs.r + rhs.g + rhs.b;
return sl > sr;
}
inline __attribute__((always_inline)) bool operator>= (const CRGB& lhs, const CRGB& rhs)
{
uint16_t sl, sr;
sl = lhs.r + lhs.g + lhs.b;
sr = rhs.r + rhs.g + rhs.b;
return sl >= sr;
}
inline __attribute__((always_inline)) bool operator<= (const CRGB& lhs, const CRGB& rhs)
{
uint16_t sl, sr;
sl = lhs.r + lhs.g + lhs.b;
sr = rhs.r + rhs.g + rhs.b;
return sl <= sr;
}
__attribute__((always_inline))
inline CRGB operator+( const CRGB& p1, const CRGB& p2)
{
return CRGB( qadd8( p1.r, p2.r),
qadd8( p1.g, p2.g),
qadd8( p1.b, p2.b));
}
__attribute__((always_inline))
inline CRGB operator-( const CRGB& p1, const CRGB& p2)
{
return CRGB( qsub8( p1.r, p2.r),
qsub8( p1.g, p2.g),
qsub8( p1.b, p2.b));
}
__attribute__((always_inline))
inline CRGB operator*( const CRGB& p1, uint8_t d)
{
return CRGB( qmul8( p1.r, d),
qmul8( p1.g, d),
qmul8( p1.b, d));
}
__attribute__((always_inline))
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)
{
return CRGB( p1.r < p2.r ? p1.r : p2.r,
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)
{
return CRGB( p1.r > p2.r ? p1.r : p2.r,
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, uint8_t d)
{
CRGB retval( p1);
retval.nscale8_video( d);
return retval;
}
/// RGB orderings, used when instantiating controllers to determine what
/// order the controller should send RGB data out in, RGB being the default
/// ordering.
enum EOrder {
RGB=0012,
RBG=0021,
GRB=0102,
GBR=0120,
BRG=0201,
BGR=0210
};
FASTLED_NAMESPACE_END
///@}
#endif

View File

@@ -0,0 +1,40 @@
#define FASTLED_INTERNAL
// Interrupt handlers cannot be defined in the header.
// They must be defined as C functions, or they won't
// be found (due to name mangling), and thus won't
// override any default weak definition.
#if defined(NRF52_SERIES)
#include "platforms/arm/nrf52/led_sysdefs_arm_nrf52.h"
#include "platforms/arm/nrf52/arbiter_nrf52.h"
uint32_t isrCount;
#ifdef __cplusplus
extern "C" {
#endif
// NOTE: Update platforms.cpp in root of FastLED library if this changes
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0)
void PWM0_IRQHandler(void) { ++isrCount; PWM_Arbiter<0>::isr_handler(); }
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1)
void PWM1_IRQHandler(void) { ++isrCount; PWM_Arbiter<1>::isr_handler(); }
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2)
void PWM2_IRQHandler(void) { ++isrCount; PWM_Arbiter<2>::isr_handler(); }
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3)
void PWM3_IRQHandler(void) { ++isrCount; PWM_Arbiter<3>::isr_handler(); }
#endif
#ifdef __cplusplus
}
#endif
#endif // defined(NRF52_SERIES)
// FASTLED_NAMESPACE_BEGIN
// FASTLED_NAMESPACE_END

View File

@@ -0,0 +1,44 @@
#ifndef __INC_PLATFORMS_H
#define __INC_PLATFORMS_H
#include "FastLED.h"
#include "fastled_config.h"
#if defined(NRF51)
#include "platforms/arm/nrf51/fastled_arm_nrf51.h"
#elif defined(NRF52_SERIES)
#include "platforms/arm/nrf52/fastled_arm_nrf52.h"
#elif defined(__MK20DX128__) || defined(__MK20DX256__)
// Include k20/T3 headers
#include "platforms/arm/k20/fastled_arm_k20.h"
#elif defined(__MK66FX1M0__) || defined(__MK64FX512__)
// Include k66/T3.6 headers
#include "platforms/arm/k66/fastled_arm_k66.h"
#elif defined(__MKL26Z64__)
// Include kl26/T-LC headers
#include "platforms/arm/kl26/fastled_arm_kl26.h"
#elif defined(__IMXRT1062__)
// teensy4
#include "platforms/arm/mxrt1062/fastled_arm_mxrt1062.h"
#elif defined(__SAM3X8E__)
// Include sam/due headers
#include "platforms/arm/sam/fastled_arm_sam.h"
#elif defined(STM32F10X_MD) || defined(__STM32F1__)
#include "platforms/arm/stm32/fastled_arm_stm32.h"
#elif defined(__SAMD21G18A__) || defined(__SAMD21J18A__) || defined(__SAMD21E17A__) || defined(__SAMD21E18A__)
#include "platforms/arm/d21/fastled_arm_d21.h"
#elif defined(__SAMD51G19A__) || defined(__SAMD51J19A__)
#include "platforms/arm/d51/fastled_arm_d51.h"
#elif defined(ESP8266)
#include "platforms/esp/8266/fastled_esp8266.h"
#elif defined(ESP32)
#include "platforms/esp/32/fastled_esp32.h"
#elif defined(ARDUINO_ARCH_APOLLO3)
#include "platforms/apollo3/fastled_apollo3.h"
#else
// AVR platforms
#include "platforms/avr/fastled_avr.h"
#endif
#endif

View File

@@ -0,0 +1,184 @@
#ifndef __INC_CLOCKLESS_APOLLO3_H
#define __INC_CLOCKLESS_APOLLO3_H
FASTLED_NAMESPACE_BEGIN
#if defined(FASTLED_APOLLO3)
// Clockless support for the SparkFun Artemis / Ambiq Micro Apollo3 Blue
// Uses SysTick to govern the pulse timing
//*****************************************************************************
//
// Code taken from Ambiq Micro's am_hal_systick.c
// and converted to inline static for speed
//
//! @brief Get the current count value in the SYSTICK.
//!
//! This function gets the current count value in the systick timer.
//!
//! @return Current count value.
//
//*****************************************************************************
__attribute__ ((always_inline)) inline static uint32_t __am_hal_systick_count() {
return SysTick->VAL;
}
#define FASTLED_HAS_CLOCKLESS 1
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
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() {
// Initialize everything
// Configure DATA_PIN for FastGPIO (settings are in fastpin_apollo3.h)
FastPin<DATA_PIN>::setOutput();
FastPin<DATA_PIN>::lo();
// Make sure the system clock is running at the full 48MHz
am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0);
// Make sure interrupts are enabled
//am_hal_interrupt_master_enable();
// Enable SysTick Interrupts in the NVIC
//NVIC_EnableIRQ(SysTick_IRQn);
// SysTick is 24-bit and counts down (not up)
// Stop the SysTick (just in case it is already running).
// This clears the ENABLE bit in the SysTick Control and Status Register (SYST_CSR).
// In Ambiq naming convention, the control register is SysTick->CTRL
am_hal_systick_stop();
// Call SysTick_Config
// This is defined in core_cm4.h
// It loads the specified LOAD value into the SysTick Reload Value Register (SYST_RVR)
// In Ambiq naming convention, the reload register is SysTick->LOAD
// It sets the SysTick interrupt priority
// It clears the SysTick Current Value Register (SYST_CVR)
// In Ambiq naming convention, the current value register is SysTick->VAL
// Finally it sets these bits in the SysTick Control and Status Register (SYST_CSR):
// CLKSOURCE: SysTick uses the processor clock
// TICKINT: When the count reaches zero, the SysTick exception (interrupt) is changed to pending
// ENABLE: Enables the counter
// SysTick_Config returns 0 if successful. 1 indicates a failure (the LOAD value was invalid).
SysTick_Config(0xFFFFFFUL); // The LOAD value needs to be 24-bit
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mWait.wait();
if(!showRGBInternal(pixels)) {
sei(); delayMicroseconds(WAIT_TIME); cli();
showRGBInternal(pixels);
}
mWait.mark();
}
template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register uint8_t & b) {
// SysTick counts down (not up) and is 24-bit
for(register uint32_t i = BITS-1; i > 0; i--) { // We could speed this up by using Bit Banding
while(__am_hal_systick_count() > next_mark) { ; } // Wait for the remainder of this cycle to complete
// Calculate next_mark (the time of the next DATA_PIN transition) by subtracting T1+T2+T3
// SysTick counts down (not up) and is 24-bit
next_mark = (__am_hal_systick_count() - (T1+T2+T3)) & 0xFFFFFFUL;
FastPin<DATA_PIN>::hi();
if(b&0x80) {
// "1 code" = longer pulse width
while((__am_hal_systick_count() - next_mark) > (T3+(3*(F_CPU/24000000)))) { ; }
FastPin<DATA_PIN>::lo();
} else {
// "0 code" = shorter pulse width
while((__am_hal_systick_count() - next_mark) > (T2+T3+(4*(F_CPU/24000000)))) { ; }
FastPin<DATA_PIN>::lo();
}
b <<= 1;
}
while(__am_hal_systick_count() > next_mark) { ; }// Wait for the remainder of this cycle to complete
// Calculate next_mark (the time of the next DATA_PIN transition) by subtracting T1+T2+T3
// SysTick counts down (not up) and is 24-bit
next_mark = (__am_hal_systick_count() - (T1+T2+T3)) & 0xFFFFFFUL;
FastPin<DATA_PIN>::hi();
if(b&0x80) {
// "1 code" = longer pulse width
while((__am_hal_systick_count() - next_mark) > (T3+(2*(F_CPU/24000000)))) { ; }
FastPin<DATA_PIN>::lo();
} else {
// "0 code" = shorter pulse width
while((__am_hal_systick_count() - next_mark) > (T2+T3+(4*(F_CPU/24000000)))) { ; }
FastPin<DATA_PIN>::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) {
// Setup the pixel controller and load/scale the first byte
pixels.preStepFirstByteDithering();
register uint8_t b = pixels.loadAndScale0();
cli();
// Calculate next_mark (the time of the next DATA_PIN transition) by subtracting T1+T2+T3
// SysTick counts down (not up) and is 24-bit
// The subtraction could underflow (wrap round) so let's mask the result to 24 bits
register uint32_t next_mark = (__am_hal_systick_count() - (T1+T2+T3)) & 0xFFFFFFUL;
while(pixels.has(1)) { // Keep going for as long as we have pixels
pixels.stepDithering();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
// Have we already missed the next_mark?
if(__am_hal_systick_count() < next_mark) {
// If we have exceeded next_mark by an excessive amount, then bail (return 0)
if((next_mark - __am_hal_systick_count()) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
}
#endif
// Write first byte, read next byte
writeBits<8+XTRA0>(next_mark, b);
b = pixels.loadAndScale1();
// Write second byte, read 3rd byte
writeBits<8+XTRA0>(next_mark, b);
b = pixels.loadAndScale2();
// Write third byte, read 1st byte of next pixel
writeBits<8+XTRA0>(next_mark, b);
b = pixels.advanceAndLoadAndScale0();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
}; // end of while(pixels.has(1))
// Unfortunately SysTick relies on an interrupt to reload it once it reaches zero
// and having interrupts disabled for most of the above means the interrupt doesn't get serviced.
// So we had better reload it here instead...
am_hal_systick_load(0xFFFFFFUL);
sei();
return (1);
}
};
#endif
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,8 @@
#ifndef __INC_FASTLED_APOLLO3_H
#define __INC_FASTLED_APOLLO3_H
#include "fastpin_apollo3.h"
#include "fastspi_apollo3.h"
#include "clockless_apollo3.h"
#endif

View File

@@ -0,0 +1,153 @@
#ifndef __INC_FASTPIN_APOLLO3_H
#define __INC_FASTPIN_APOLLO3_H
FASTLED_NAMESPACE_BEGIN
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
#warning "Software pin support forced, pin access will be slightly slower."
#define NO_HARDWARE_PIN_SUPPORT
#undef HAS_HARDWARE_PIN_SUPPORT
#else
template<uint8_t PIN, uint8_t PAD> class _APOLLO3PIN {
public:
typedef volatile uint32_t * port_ptr_t;
typedef uint32_t port_t;
inline static void setOutput() { pinMode(PIN, OUTPUT); am_hal_gpio_fastgpio_enable(PAD); }
inline static void setInput() { am_hal_gpio_fastgpio_disable(PAD); pinMode(PIN, INPUT); }
inline static void hi() __attribute__ ((always_inline)) { am_hal_gpio_fastgpio_set(PAD); }
inline static void lo() __attribute__ ((always_inline)) { am_hal_gpio_fastgpio_clr(PAD); }
inline static void set(register port_t val) __attribute__ ((always_inline)) { if(val) { am_hal_gpio_fastgpio_set(PAD); } else { am_hal_gpio_fastgpio_clr(PAD); } }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline static void toggle() __attribute__ ((always_inline)) { if( am_hal_gpio_fastgpio_read(PAD)) { lo(); } else { hi(); } }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { set(val); }
inline static port_t hival() __attribute__ ((always_inline)) { return 0; }
inline static port_t loval() __attribute__ ((always_inline)) { return 0; }
inline static port_ptr_t port() __attribute__ ((always_inline)) { return NULL; }
inline static port_t mask() __attribute__ ((always_inline)) { return 0; }
};
// For the Apollo3 we need to define both the pin number and the associated pad
// to avoid having to use ap3_gpio_pin2pad for fastgpio (which would slow things down)
#define _FL_DEFPIN(PIN, PAD) template<> class FastPin<PIN> : public _APOLLO3PIN<PIN, PAD> {};
// Actual (pin, pad) definitions
#if defined(ARDUINO_SFE_EDGE)
#define MAX_PIN 49
_FL_DEFPIN(0, 0); _FL_DEFPIN(1, 1); _FL_DEFPIN(3, 3); _FL_DEFPIN(4, 4);
_FL_DEFPIN(5, 5); _FL_DEFPIN(6, 6); _FL_DEFPIN(7, 7); _FL_DEFPIN(8, 8); _FL_DEFPIN(9, 9);
_FL_DEFPIN(10, 10); _FL_DEFPIN(11, 11); _FL_DEFPIN(12, 12); _FL_DEFPIN(13, 13); _FL_DEFPIN(14, 14);
_FL_DEFPIN(15, 15); _FL_DEFPIN(17, 17);
_FL_DEFPIN(20, 20); _FL_DEFPIN(21, 21); _FL_DEFPIN(22, 22); _FL_DEFPIN(23, 23); _FL_DEFPIN(24, 24);
_FL_DEFPIN(25, 25); _FL_DEFPIN(26, 26); _FL_DEFPIN(27, 27); _FL_DEFPIN(28, 28); _FL_DEFPIN(29, 29);
_FL_DEFPIN(33, 33);
_FL_DEFPIN(36, 36); _FL_DEFPIN(37, 37); _FL_DEFPIN(38, 38); _FL_DEFPIN(39, 39);
_FL_DEFPIN(40, 40); _FL_DEFPIN(42, 42); _FL_DEFPIN(43, 43); _FL_DEFPIN(44, 44);
_FL_DEFPIN(46, 46); _FL_DEFPIN(47, 47); _FL_DEFPIN(48, 48); _FL_DEFPIN(49, 49);
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ARDUINO_SFE_EDGE2)
#define MAX_PIN 49
_FL_DEFPIN(0, 0);
_FL_DEFPIN(5, 5); _FL_DEFPIN(6, 6); _FL_DEFPIN(7, 7); _FL_DEFPIN(8, 8); _FL_DEFPIN(9, 9);
_FL_DEFPIN(11, 11); _FL_DEFPIN(12, 12); _FL_DEFPIN(13, 13); _FL_DEFPIN(14, 14);
_FL_DEFPIN(15, 15); _FL_DEFPIN(16, 16); _FL_DEFPIN(17, 17); _FL_DEFPIN(18, 18); _FL_DEFPIN(19, 19);
_FL_DEFPIN(20, 20); _FL_DEFPIN(21, 21); _FL_DEFPIN(23, 23);
_FL_DEFPIN(25, 25); _FL_DEFPIN(26, 26); _FL_DEFPIN(27, 27); _FL_DEFPIN(28, 28); _FL_DEFPIN(29, 29);
_FL_DEFPIN(31, 31); _FL_DEFPIN(32, 32); _FL_DEFPIN(33, 33); _FL_DEFPIN(34, 34);
_FL_DEFPIN(35, 35); _FL_DEFPIN(37, 37); _FL_DEFPIN(39, 39);
_FL_DEFPIN(40, 40); _FL_DEFPIN(41, 41); _FL_DEFPIN(42, 42); _FL_DEFPIN(43, 43); _FL_DEFPIN(44, 44);
_FL_DEFPIN(45, 45); _FL_DEFPIN(48, 48); _FL_DEFPIN(49, 49);
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ARDUINO_AM_AP3_SFE_BB_ARTEMIS)
#define MAX_PIN 31
_FL_DEFPIN(0, 25); _FL_DEFPIN(1, 24); _FL_DEFPIN(2, 35); _FL_DEFPIN(3, 4); _FL_DEFPIN(4, 22);
_FL_DEFPIN(5, 23); _FL_DEFPIN(6, 27); _FL_DEFPIN(7, 28); _FL_DEFPIN(8, 32); _FL_DEFPIN(9, 12);
_FL_DEFPIN(10, 13); _FL_DEFPIN(11, 7); _FL_DEFPIN(12, 6); _FL_DEFPIN(13, 5); _FL_DEFPIN(14, 40);
_FL_DEFPIN(15, 39); _FL_DEFPIN(16, 29); _FL_DEFPIN(17, 11); _FL_DEFPIN(18, 34); _FL_DEFPIN(19, 33);
_FL_DEFPIN(20, 16); _FL_DEFPIN(21, 31); _FL_DEFPIN(22, 48); _FL_DEFPIN(23, 49); _FL_DEFPIN(24, 8);
_FL_DEFPIN(25, 9); _FL_DEFPIN(26, 10); _FL_DEFPIN(27, 38); _FL_DEFPIN(28, 42); _FL_DEFPIN(29, 43);
_FL_DEFPIN(30, 36); _FL_DEFPIN(31, 37);
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ARDUINO_AM_AP3_SFE_BB_ARTEMIS_NANO)
#define MAX_PIN 23
_FL_DEFPIN(0, 13); _FL_DEFPIN(1, 33); _FL_DEFPIN(2, 11); _FL_DEFPIN(3, 29); _FL_DEFPIN(4, 18);
_FL_DEFPIN(5, 31); _FL_DEFPIN(6, 43); _FL_DEFPIN(7, 42); _FL_DEFPIN(8, 38); _FL_DEFPIN(9, 39);
_FL_DEFPIN(10, 40); _FL_DEFPIN(11, 5); _FL_DEFPIN(12, 7); _FL_DEFPIN(13, 6); _FL_DEFPIN(14, 35);
_FL_DEFPIN(15, 32); _FL_DEFPIN(16, 12); _FL_DEFPIN(17, 32); _FL_DEFPIN(18, 12); _FL_DEFPIN(19, 19);
_FL_DEFPIN(20, 48); _FL_DEFPIN(21, 49); _FL_DEFPIN(22, 36); _FL_DEFPIN(23, 37);
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ARDUINO_AM_AP3_SFE_THING_PLUS)
#define MAX_PIN 28
_FL_DEFPIN(0, 25); _FL_DEFPIN(1, 24); _FL_DEFPIN(2, 44); _FL_DEFPIN(3, 35); _FL_DEFPIN(4, 4);
_FL_DEFPIN(5, 22); _FL_DEFPIN(6, 23); _FL_DEFPIN(7, 27); _FL_DEFPIN(8, 28); _FL_DEFPIN(9, 32);
_FL_DEFPIN(10, 14); _FL_DEFPIN(11, 7); _FL_DEFPIN(12, 6); _FL_DEFPIN(13, 5); _FL_DEFPIN(14, 40);
_FL_DEFPIN(15, 39); _FL_DEFPIN(16, 43); _FL_DEFPIN(17, 42); _FL_DEFPIN(18, 26); _FL_DEFPIN(19, 33);
_FL_DEFPIN(20, 13); _FL_DEFPIN(21, 11); _FL_DEFPIN(22, 29); _FL_DEFPIN(23, 12); _FL_DEFPIN(24, 31);
_FL_DEFPIN(25, 48); _FL_DEFPIN(26, 49); _FL_DEFPIN(27, 36); _FL_DEFPIN(28, 37);
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ARDUINO_AM_AP3_SFE_BB_ARTEMIS_ATP) || defined(ARDUINO_SFE_ARTEMIS)
#define MAX_PIN 49
_FL_DEFPIN(0, 0); _FL_DEFPIN(1, 1); _FL_DEFPIN(2, 2); _FL_DEFPIN(3, 3); _FL_DEFPIN(4, 4);
_FL_DEFPIN(5, 5); _FL_DEFPIN(6, 6); _FL_DEFPIN(7, 7); _FL_DEFPIN(8, 8); _FL_DEFPIN(9, 9);
_FL_DEFPIN(10, 10); _FL_DEFPIN(11, 11); _FL_DEFPIN(12, 12); _FL_DEFPIN(13, 13); _FL_DEFPIN(14, 14);
_FL_DEFPIN(15, 15); _FL_DEFPIN(16, 16); _FL_DEFPIN(17, 17); _FL_DEFPIN(18, 18); _FL_DEFPIN(19, 19);
_FL_DEFPIN(20, 20); _FL_DEFPIN(21, 21); _FL_DEFPIN(22, 22); _FL_DEFPIN(23, 23); _FL_DEFPIN(24, 24);
_FL_DEFPIN(25, 25); _FL_DEFPIN(26, 26); _FL_DEFPIN(27, 27); _FL_DEFPIN(28, 28); _FL_DEFPIN(29, 29);
_FL_DEFPIN(31, 31); _FL_DEFPIN(32, 32); _FL_DEFPIN(33, 33); _FL_DEFPIN(34, 34);
_FL_DEFPIN(35, 35); _FL_DEFPIN(36, 36); _FL_DEFPIN(37, 37); _FL_DEFPIN(38, 38); _FL_DEFPIN(39, 39);
_FL_DEFPIN(40, 40); _FL_DEFPIN(41, 41); _FL_DEFPIN(42, 42); _FL_DEFPIN(43, 43); _FL_DEFPIN(44, 44);
_FL_DEFPIN(45, 45); _FL_DEFPIN(47, 47); _FL_DEFPIN(48, 48); _FL_DEFPIN(49, 49);
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ARDUINO_AM_AP3_SFE_ARTEMIS_DK)
#define MAX_PIN 49
_FL_DEFPIN(0, 0); _FL_DEFPIN(1, 1); _FL_DEFPIN(2, 2); _FL_DEFPIN(3, 3); _FL_DEFPIN(4, 4);
_FL_DEFPIN(5, 5); _FL_DEFPIN(6, 6); _FL_DEFPIN(7, 7); _FL_DEFPIN(8, 8); _FL_DEFPIN(9, 9);
_FL_DEFPIN(10, 10); _FL_DEFPIN(11, 11); _FL_DEFPIN(12, 12); _FL_DEFPIN(13, 13); _FL_DEFPIN(14, 14);
_FL_DEFPIN(15, 15); _FL_DEFPIN(16, 16); _FL_DEFPIN(17, 17); _FL_DEFPIN(18, 18); _FL_DEFPIN(19, 19);
_FL_DEFPIN(20, 20); _FL_DEFPIN(21, 21); _FL_DEFPIN(22, 22); _FL_DEFPIN(23, 23); _FL_DEFPIN(24, 24);
_FL_DEFPIN(25, 25); _FL_DEFPIN(26, 26); _FL_DEFPIN(27, 27); _FL_DEFPIN(28, 28); _FL_DEFPIN(29, 29);
_FL_DEFPIN(31, 31); _FL_DEFPIN(32, 32); _FL_DEFPIN(33, 33); _FL_DEFPIN(34, 34);
_FL_DEFPIN(35, 35); _FL_DEFPIN(36, 36); _FL_DEFPIN(37, 37); _FL_DEFPIN(38, 38); _FL_DEFPIN(39, 39);
_FL_DEFPIN(40, 40); _FL_DEFPIN(41, 41); _FL_DEFPIN(42, 42); _FL_DEFPIN(43, 43); _FL_DEFPIN(44, 44);
_FL_DEFPIN(45, 45); _FL_DEFPIN(47, 47); _FL_DEFPIN(48, 48); _FL_DEFPIN(49, 49);
#define HAS_HARDWARE_PIN_SUPPORT 1
#else
#error "Unrecognised APOLLO3 board!"
#endif
#endif // FASTLED_FORCE_SOFTWARE_PINS
FASTLED_NAMESPACE_END
#endif // __INC_FASTPIN_AVR_H

View File

@@ -0,0 +1,134 @@
#ifndef __INC_FASTSPI_APOLLO3_H
#define __INC_FASTSPI_APOLLO3_H
// This is the implementation of fastspi for the Apollo3.
// It uses fastgpio instead of actual SPI, which means you can use it on all pins.
// It can run slightly faster than the default fastpin (bit banging).
#include "FastLED.h"
FASTLED_NAMESPACE_BEGIN
#if defined(FASTLED_APOLLO3)
#define FASTLED_ALL_PINS_HARDWARE_SPI
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class APOLLO3HardwareSPIOutput {
Selectable *m_pSelect;
public:
APOLLO3HardwareSPIOutput() { m_pSelect = NULL; }
APOLLO3HardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
// set the object representing the selectable
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
// initialize the pins for fastgpio
void init() {
FastPin<_CLOCK_PIN>::setOutput();
FastPin<_CLOCK_PIN>::lo();
FastPin<_DATA_PIN>::setOutput();
FastPin<_DATA_PIN>::lo();
}
// latch the CS select
void inline select() { /* TODO */ }
// release the CS select
void inline release() { /* TODO */ }
// wait until all queued up data has been written
static void waitFully() { /* TODO */ }
// write a byte as bits
static void writeByte(uint8_t b) {
writeBit<7>(b);
writeBit<6>(b);
writeBit<5>(b);
writeBit<4>(b);
writeBit<3>(b);
writeBit<2>(b);
writeBit<1>(b);
writeBit<0>(b);
}
// write a word out via SPI (returns immediately on writing register)
static void writeWord(uint16_t w) {
writeByte((uint8_t)((w >> 8) & 0xff));
writeByte((uint8_t)(w & 0xff));
}
// 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) {
//waitFully();
if(b & (1 << BIT)) {
FastPin<_DATA_PIN>::hi();
} else {
FastPin<_DATA_PIN>::lo();
}
FastPin<_CLOCK_PIN>::hi();
for (uint32_t d = (_SPI_CLOCK_DIVIDER >> 1); d > 0; d--) { __NOP(); }
FastPin<_CLOCK_PIN>::lo();
for (uint32_t d = (_SPI_CLOCK_DIVIDER >> 1); d > 0; d--) { __NOP(); }
}
// 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;
while(pixels.has(1)) {
if(FLAGS & FLAG_START_BIT) {
writeBit<0>(1);
}
writeByte(D::adjust(pixels.loadAndScale0()));
writeByte(D::adjust(pixels.loadAndScale1()));
writeByte(D::adjust(pixels.loadAndScale2()));
pixels.advanceData();
pixels.stepDithering();
}
D::postBlock(len);
//waitFully();
release();
}
};
#endif
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,39 @@
#ifndef __INC_LED_SYSDEFS_APOLLO3_H
#define __INC_LED_SYSDEFS_APOLLO3_H
#define FASTLED_APOLLO3
#ifndef INTERRUPT_THRESHOLD
#define INTERRUPT_THRESHOLD 1
#endif
// Default to allowing interrupts
#ifndef FASTLED_ALLOW_INTERRUPTS
#define FASTLED_ALLOW_INTERRUPTS 1
#endif
#if FASTLED_ALLOW_INTERRUPTS == 1
#define FASTLED_ACCURATE_CLOCK
#endif
#ifndef F_CPU
#define F_CPU 48000000
#endif
// Default to NOT using PROGMEM
#ifndef FASTLED_USE_PROGMEM
#define FASTLED_USE_PROGMEM 0
#endif
// data type defs
typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
#define FASTLED_NO_PINMAP
// reusing/abusing cli/sei defs for due
// These should be fine for the Apollo3. It has its own defines in cmsis_gcc.h
#define cli() __disable_irq(); //__disable_fault_irq();
#define sei() __enable_irq(); //__enable_fault_irq();
#endif

View File

@@ -0,0 +1,389 @@
#ifndef __INC_M0_CLOCKLESS_H
#define __INC_M0_CLOCKLESS_H
struct M0ClocklessData {
uint8_t d[3];
uint8_t e[3];
uint8_t adj;
uint8_t pad;
uint32_t s[3];
};
template<int HI_OFFSET, int LO_OFFSET, int T1, int T2, int T3, EOrder RGB_ORDER, int WAIT_TIME>int
showLedData(volatile uint32_t *_port, uint32_t _bitmask, const uint8_t *_leds, uint32_t num_leds, struct M0ClocklessData *pData) {
// Lo register variables
register uint32_t scratch=0;
register struct M0ClocklessData *base = pData;
register volatile uint32_t *port = _port;
register uint32_t d=0;
register uint32_t counter=num_leds;
register uint32_t bn=0;
register uint32_t b=0;
register uint32_t bitmask = _bitmask;
// high register variable
register const uint8_t *leds = _leds;
#if (FASTLED_SCALE8_FIXED == 1)
++pData->s[0];
++pData->s[1];
++pData->s[2];
#endif
asm __volatile__ (
///////////////////////////////////////////////////////////////////////////
//
// asm macro definitions - used to assemble the clockless output
//
".ifnotdef fl_delay_def;"
#ifdef FASTLED_ARM_M0_PLUS
" .set fl_is_m0p, 1;"
" .macro m0pad;"
" nop;"
" .endm;"
#else
" .set fl_is_m0p, 0;"
" .macro m0pad;"
" .endm;"
#endif
" .set fl_delay_def, 1;"
" .set fl_delay_mod, 4;"
" .if fl_is_m0p == 1;"
" .set fl_delay_mod, 3;"
" .endif;"
" .macro fl_delay dtime, reg=r0;"
" .if (\\dtime > 0);"
" .set dcycle, (\\dtime / fl_delay_mod);"
" .set dwork, (dcycle * fl_delay_mod);"
" .set drem, (\\dtime - dwork);"
" .rept (drem);"
" nop;"
" .endr;"
" .if dcycle > 0;"
" mov \\reg, #dcycle;"
" delayloop_\\@:;"
" sub \\reg, #1;"
" bne delayloop_\\@;"
" .if fl_is_m0p == 0;"
" nop;"
" .endif;"
" .endif;"
" .endif;"
" .endm;"
" .macro mod_delay dtime,b1,b2,reg;"
" .set adj, (\\b1 + \\b2);"
" .if adj < \\dtime;"
" .set dtime2, (\\dtime - adj);"
" fl_delay dtime2, \\reg;"
" .endif;"
" .endm;"
// check the bit and drop the line low if it isn't set
" .macro qlo4 b,bitmask,port,loff ;"
" lsl \\b, #1 ;"
" bcs skip_\\@ ;"
" str \\bitmask, [\\port, \\loff] ;"
" skip_\\@: ;"
" m0pad;"
" .endm ;"
// set the pin hi or low (determined by the offset passed in )
" .macro qset2 bitmask,port,loff;"
" str \\bitmask, [\\port, \\loff];"
" m0pad;"
" .endm;"
// Load up the next led byte to work with, put it in bn
" .macro loadleds3 leds, bn, rled, scratch;"
" mov \\scratch, \\leds;"
" ldrb \\bn, [\\scratch, \\rled];"
" .endm;"
// check whether or not we should dither
" .macro loaddither7 bn,d,base,rdither;"
" ldrb \\d, [\\base, \\rdither];"
" lsl \\d, #24;" //; shift high for the qadd w/bn
" lsl \\bn, #24;" //; shift high for the qadd w/d
" bne chkskip_\\@;" //; if bn==0, clear d;"
" eor \\d, \\d;" //; clear d;"
" m0pad;"
" chkskip_\\@:;"
" .endm;"
// Do the qadd8 for dithering -- there's two versions of this. The m0 version
// takes advantage of the 3 cycle branch to do two things after the branch,
// while keeping timing constant. The m0+, however, branches in 2 cycles, so
// we have to work around that a bit more. This is one of the few times
// where the m0 will actually be _more_ efficient than the m0+
" .macro dither5 bn,d;"
" .syntax unified;"
" .if fl_is_m0p == 0;"
" adds \\bn, \\d;" // do the add
" bcc dither5_1_\\@;"
" mvns \\bn, \\bn;" // set the low 24bits ot 1's
" lsls \\bn, \\bn, #24;" // move low 8 bits to the high bits
" dither5_1_\\@:;"
" nop;" // nop to keep timing in line
" .else;"
" adds \\bn, \\d;" // do the add"
" bcc dither5_2_\\@;"
" mvns \\bn, \\bn;" // set the low 24bits ot 1's
" dither5_2_\\@:;"
" bcc dither5_3_\\@;"
" lsls \\bn, \\bn, #24;" // move low 8 bits to the high bits
" dither5_3_\\@:;"
" .endif;"
" .syntax divided;"
" .endm;"
// Do our scaling
" .macro scale4 bn, base, scale, scratch;"
" ldr \\scratch, [\\base, \\scale];"
" lsr \\bn, \\bn, #24;" // bring bn back down to its low 8 bits
" mul \\bn, \\scratch;" // do the multiply
" .endm;"
// swap bn into b
" .macro swapbbn1 b,bn;"
" lsl \\b, \\bn, #16;" // put the 8 bits we want for output high
" .endm;"
// adjust the dithering value for the next time around (load e from memory
// to do the math)
" .macro adjdither7 base,d,rled,eoffset,scratch;"
" ldrb \\d, [\\base, \\rled];"
" ldrb \\scratch,[\\base,\\eoffset];" // load e
" .syntax unified;"
" subs \\d, \\scratch, \\d;" // d=e-d
" .syntax divided;"
" strb \\d, [\\base, \\rled];" // save d
" .endm;"
// increment the led pointer (base+6 has what we're incrementing by)
" .macro incleds3 leds, base, scratch;"
" ldrb \\scratch, [\\base, #6];" // load incremen
" add \\leds, \\leds, \\scratch;" // update leds pointer
" .endm;"
// compare and loop
" .macro cmploop5 counter,label;"
" .syntax unified;"
" subs \\counter, #1;"
" .syntax divided;"
" beq done_\\@;"
" m0pad;"
" b \\label;"
" done_\\@:;"
" .endm;"
" .endif;"
);
#define M0_ASM_ARGS : \
[leds] "+h" (leds), \
[counter] "+l" (counter), \
[scratch] "+l" (scratch), \
[d] "+l" (d), \
[bn] "+l" (bn), \
[b] "+l" (b) \
: \
[port] "l" (port), \
[base] "l" (base), \
[bitmask] "l" (bitmask), \
[hi_off] "I" (HI_OFFSET), \
[lo_off] "I" (LO_OFFSET), \
[led0] "I" (RO(0)), \
[led1] "I" (RO(1)), \
[led2] "I" (RO(2)), \
[e0] "I" (3+RO(0)), \
[e1] "I" (3+RO(1)), \
[e2] "I" (3+RO(2)), \
[scale0] "I" (4*(2+RO(0))), \
[scale1] "I" (4*(2+RO(1))), \
[scale2] "I" (4*(2+RO(2))), \
[T1] "I" (T1), \
[T2] "I" (T2), \
[T3] "I" (T3) \
:
/////////////////////////////////////////////////////////////////////////
// now for some convinience macros to make building our lines a bit cleaner
#define LOOP " loop_%=:"
#define HI2 " qset2 %[bitmask], %[port], %[hi_off];"
#define _D1 " mod_delay %c[T1],2,0,%[scratch];"
#define QLO4 " qlo4 %[b],%[bitmask],%[port], %[lo_off];"
#define LOADLEDS3(X) " loadleds3 %[leds], %[bn], %[led" #X "] ,%[scratch];"
#define _D2(ADJ) " mod_delay %c[T2],4," #ADJ ",%[scratch];"
#define LO2 " qset2 %[bitmask], %[port], %[lo_off];"
#define _D3(ADJ) " mod_delay %c[T3],2," #ADJ ",%[scratch];"
#define LOADDITHER7(X) " loaddither7 %[bn], %[d], %[base], %[led" #X "];"
#define DITHER5 " dither5 %[bn], %[d];"
#define SCALE4(X) " scale4 %[bn], %[base], %[scale" #X "], %[scratch];"
#define SWAPBBN1 " swapbbn1 %[b], %[bn];"
#define ADJDITHER7(X) " adjdither7 %[base],%[d],%[led" #X "],%[e" #X "],%[scratch];"
#define INCLEDS3 " incleds3 %[leds],%[base],%[scratch];"
#define CMPLOOP5 " cmploop5 %[counter], loop_%=;"
#define NOTHING ""
#if (defined(SEI_CHK) && (FASTLED_ALLOW_INTERRUPTS == 1))
// We're allowing interrupts and have hardware timer support defined -
// track the loop outside the asm code, to allow inserting the interrupt
// overrun checks.
asm __volatile__ (
// pre-load byte 0
LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1
M0_ASM_ARGS);
do {
asm __volatile__ (
// Write out byte 0, prepping byte 1
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0)
HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0)
HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
// Write out byte 1, prepping byte 2
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0)
HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0)
HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
// Write out byte 2, prepping byte 0
HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0)
HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0)
HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0)
HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5)
M0_ASM_ARGS
);
SEI_CHK; INNER_SEI; --counter; CLI_CHK;
} while(counter);
#elif (FASTLED_ALLOW_INTERRUPTS == 1)
// We're allowing interrupts - track the loop outside the asm code, and
// re-enable interrupts in between each iteration.
asm __volatile__ (
// pre-load byte 0
LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1
M0_ASM_ARGS);
do {
asm __volatile__ (
// Write out byte 0, prepping byte 1
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0)
HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0)
HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
// Write out byte 1, prepping byte 2
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0)
HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0)
HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0)
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
// Write out byte 2, prepping byte 0
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0)
HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0)
HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5)
M0_ASM_ARGS
);
uint32_t ticksBeforeInterrupts = SysTick->VAL;
sei();
--counter;
cli();
// If more than 45 uSecs have elapsed, give up on this frame and start over.
// Note: this isn't completely correct. It's possible that more than one
// millisecond will elapse, and so SysTick->VAL will lap
// ticksBeforeInterrupts.
// Note: ticksBeforeInterrupts DECREASES
const uint32_t kTicksPerMs = VARIANT_MCK / 1000;
const uint32_t kTicksPerUs = kTicksPerMs / 1000;
const uint32_t kTicksIn45us = kTicksPerUs * 45;
const uint32_t currentTicks = SysTick->VAL;
if (ticksBeforeInterrupts < currentTicks) {
// Timer started over
if ((ticksBeforeInterrupts + (kTicksPerMs - currentTicks)) > kTicksIn45us) {
return 0;
}
} else {
if ((ticksBeforeInterrupts - currentTicks) > kTicksIn45us) {
return 0;
}
}
} while(counter);
#else
// We're not allowing interrupts - run the entire loop in asm to keep things
// as tight as possible. In an ideal world, we should be pushing out ws281x
// leds (or other 3-wire leds) with zero gaps between pixels.
asm __volatile__ (
// pre-load byte 0
LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1
// loop over writing out the data
LOOP
// Write out byte 0, prepping byte 1
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0)
HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0)
HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
// Write out byte 1, prepping byte 2
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0)
HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0)
HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0)
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
// Write out byte 2, prepping byte 0
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0)
HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0)
HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0)
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5) CMPLOOP5
M0_ASM_ARGS
);
#endif
return num_leds;
}
#endif

View File

@@ -0,0 +1,61 @@
#ifndef __INC_CLOCKLESS_ARM_D21
#define __INC_CLOCKLESS_ARM_D21
#include "../common/m0clockless.h"
FASTLED_NAMESPACE_BEGIN
#define FASTLED_HAS_CLOCKLESS 1
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPinBB<DATA_PIN>::port_t data_t;
data_t mPinMask;
data_ptr_t mPort;
CMinWait<WAIT_TIME> mWait;
public:
virtual void init() {
FastPinBB<DATA_PIN>::setOutput();
mPinMask = FastPinBB<DATA_PIN>::mask();
mPort = FastPinBB<DATA_PIN>::port();
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mWait.wait();
cli();
if(!showRGBInternal(pixels)) {
sei(); delayMicroseconds(WAIT_TIME); cli();
showRGBInternal(pixels);
}
sei();
mWait.mark();
}
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
// gcc will use register Y for the this pointer.
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
struct M0ClocklessData data;
data.d[0] = pixels.d[0];
data.d[1] = pixels.d[1];
data.d[2] = pixels.d[2];
data.s[0] = pixels.mScale[0];
data.s[1] = pixels.mScale[1];
data.s[2] = pixels.mScale[2];
data.e[0] = pixels.e[0];
data.e[1] = pixels.e[1];
data.e[2] = pixels.e[2];
data.adj = pixels.mAdvance;
typename FastPin<DATA_PIN>::port_ptr_t portBase = FastPin<DATA_PIN>::port();
return showLedData<8,4,T1,T2,T3,RGB_ORDER, WAIT_TIME>(portBase, FastPin<DATA_PIN>::mask(), pixels.mData, pixels.mLen, &data);
}
};
FASTLED_NAMESPACE_END
#endif // __INC_CLOCKLESS_ARM_D21

View File

@@ -0,0 +1,7 @@
#ifndef __INC_FASTLED_ARM_D21_H
#define __INC_FASTLED_ARM_D21_H
#include "fastpin_arm_d21.h"
#include "clockless_arm_d21.h"
#endif

View File

@@ -0,0 +1,274 @@
#ifndef __INC_FASTPIN_ARM_SAM_H
#define __INC_FASTPIN_ARM_SAM_H
FASTLED_NAMESPACE_BEGIN
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
#warning "Software pin support forced, pin access will be slightly slower."
#define NO_HARDWARE_PIN_SUPPORT
#undef HAS_HARDWARE_PIN_SUPPORT
#else
/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
/// The registers are data output, set output, clear output, toggle output, input, and direction
template<uint8_t PIN, uint8_t _BIT, uint32_t _MASK, int _GRP> class _ARMPIN {
public:
typedef volatile uint32_t * port_ptr_t;
typedef uint32_t port_t;
#if 0
inline static void setOutput() {
if(_BIT<8) {
_CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4));
} else {
_CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4));
}
}
inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
#endif
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
inline static void hi() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTSET.reg = _MASK; }
inline static void lo() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTCLR.reg = _MASK; }
inline static void set(register port_t val) __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUT.reg = val; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline static void toggle() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTTGL.reg = _MASK; }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
inline static port_t hival() __attribute__ ((always_inline)) { return PORT_IOBUS->Group[_GRP].OUT.reg | _MASK; }
inline static port_t loval() __attribute__ ((always_inline)) { return PORT_IOBUS->Group[_GRP].OUT.reg & ~_MASK; }
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUT.reg; }
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUTSET.reg; }
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUTCLR.reg; }
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
};
#define _R(T) struct __gen_struct_ ## T
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline volatile PortGroup * r() { return T; } };
#define _FL_IO(L) _RD32(GPIO ## L)
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1 << BIT, L> {};
// Actual pin definitions
#if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS)
#define MAX_PIN 17
_FL_DEFPIN( 8,23,1);
_FL_DEFPIN( 0, 9,1); _FL_DEFPIN( 1, 8,1); _FL_DEFPIN( 2, 2,1); _FL_DEFPIN( 3, 3,1);
_FL_DEFPIN( 6, 5,0); _FL_DEFPIN( 9, 6,0); _FL_DEFPIN(10, 7,0); _FL_DEFPIN(12, 2,0);
_FL_DEFPIN(A6, 9,1); _FL_DEFPIN(A7, 8,1); _FL_DEFPIN(A5, 2,1); _FL_DEFPIN(A4, 3,1);
_FL_DEFPIN(A1, 5,0); _FL_DEFPIN(A2, 6,0); _FL_DEFPIN(A3, 7,0); _FL_DEFPIN(A0, 2,0);
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ADAFRUIT_HALLOWING)
#define MAX_PIN 20
// 0 & 1
_FL_DEFPIN( 0, 9, 0); _FL_DEFPIN( 1, 10, 0);
// 2, 3, 4
_FL_DEFPIN( 2, 14, 0); _FL_DEFPIN( 3, 11, 0); _FL_DEFPIN( 4, 8, 0);
// 5, 6, 7
_FL_DEFPIN( 5, 15, 0); _FL_DEFPIN( 6, 18, 0); _FL_DEFPIN( 7, 0, 0);
// 8, 9, 10
_FL_DEFPIN( 8, 12, 0); _FL_DEFPIN( 9, 19, 0); _FL_DEFPIN(10, 20, 0);
// 11, 12, 13
_FL_DEFPIN(11, 21, 0); _FL_DEFPIN(12, 22, 0); _FL_DEFPIN(13, 23, 0);
// 14, 15, 16 (A0 - A2)
_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 8, 1); _FL_DEFPIN(16, 9, 1);
// 17, 18, 19 (A3 - A5)
_FL_DEFPIN(17, 4, 0); _FL_DEFPIN(18, 5, 0); _FL_DEFPIN(19, 6, 0);
#define SPI_DATA PIN_SPI_MOSI
#define SPI_CLOCK PIN_SPI_SCK
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(SEEED_XIAO_M0)
#define MAX_PIN 10
_FL_DEFPIN( 0, 2,0); _FL_DEFPIN( 1, 4,0); _FL_DEFPIN( 2,10,0); _FL_DEFPIN( 3,11,0);
_FL_DEFPIN( 4, 8,0); _FL_DEFPIN( 5, 9,0); _FL_DEFPIN( 6, 8,1); _FL_DEFPIN( 7, 9,1);
_FL_DEFPIN( 8, 7,0); _FL_DEFPIN( 9, 5,0); _FL_DEFPIN(10, 6,0);
#define SPI_DATA 9
#define SPI_CLOCK 8
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ARDUINO_SEEED_ZERO)
#define MAX_PIN 24
_FL_DEFPIN( 0,11,0); _FL_DEFPIN( 1,10,0); _FL_DEFPIN( 2,14,0); _FL_DEFPIN( 3,9,0);
_FL_DEFPIN( 4,8,0); _FL_DEFPIN( 5,15,0); _FL_DEFPIN( 6,20,0); _FL_DEFPIN( 7,21,0);
_FL_DEFPIN( 8,6,0); _FL_DEFPIN( 9,7,0); _FL_DEFPIN( 10,18,0); _FL_DEFPIN( 11,16,0);
_FL_DEFPIN( 12,19,0); _FL_DEFPIN( 13,17,0); _FL_DEFPIN( 14,2,0); _FL_DEFPIN( 15,8,1);
_FL_DEFPIN( 16,9,1); _FL_DEFPIN( 17,4,0); _FL_DEFPIN( 18,5,0); _FL_DEFPIN( 19,2,1);
_FL_DEFPIN( 20,22,0); _FL_DEFPIN( 21,23,0); _FL_DEFPIN( 22,12,0);
_FL_DEFPIN( 23,10,1);//MOSI
_FL_DEFPIN( 24,11,1);//SCK
#define SPI_DATA 23
#define SPI_CLOCK 24
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ARDUINO_SAMD_ZERO)
#define MAX_PIN 42
_FL_DEFPIN( 0,10,0); _FL_DEFPIN( 1,11,0); _FL_DEFPIN( 2, 8,0); _FL_DEFPIN( 3, 9,0);
_FL_DEFPIN( 4,14,0); _FL_DEFPIN( 5,15,0); _FL_DEFPIN( 6,20,0); _FL_DEFPIN( 7,21,0);
_FL_DEFPIN( 8, 6,0); _FL_DEFPIN( 9, 7,0); _FL_DEFPIN(10,18,0); _FL_DEFPIN(11,16,0);
_FL_DEFPIN(12,19,0); _FL_DEFPIN(13,17,0); _FL_DEFPIN(14, 2,0); _FL_DEFPIN(15, 8,1);
_FL_DEFPIN(16, 9,1); _FL_DEFPIN(17, 4,0); _FL_DEFPIN(18, 5,0); _FL_DEFPIN(19, 2,1);
_FL_DEFPIN(20,22,0); _FL_DEFPIN(21,23,0); _FL_DEFPIN(22,12,0); _FL_DEFPIN(23,11,1);
_FL_DEFPIN(24,10,1); _FL_DEFPIN(25, 3,1); _FL_DEFPIN(26,27,0); _FL_DEFPIN(27,28,0);
_FL_DEFPIN(28,24,0); _FL_DEFPIN(29,25,0); _FL_DEFPIN(30,22,1); _FL_DEFPIN(31,23,1);
_FL_DEFPIN(32,22,0); _FL_DEFPIN(33,23,0); _FL_DEFPIN(34,19,0); _FL_DEFPIN(35,16,0);
_FL_DEFPIN(36,18,0); _FL_DEFPIN(37,17,0); _FL_DEFPIN(38,13,0); _FL_DEFPIN(39,21,0);
_FL_DEFPIN(40, 6,0); _FL_DEFPIN(41, 7,0); _FL_DEFPIN(42, 3,0);
#define SPI_DATA 24
#define SPI_CLOCK 23
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ARDUINO_SODAQ_AUTONOMO)
#define MAX_PIN 56
_FL_DEFPIN( 0, 9,0); _FL_DEFPIN( 1,10,0); _FL_DEFPIN( 2,11,0); _FL_DEFPIN( 3,10,1);
_FL_DEFPIN( 4,11,1); _FL_DEFPIN( 5,12,1); _FL_DEFPIN( 6,13,1); _FL_DEFPIN( 7,14,1);
_FL_DEFPIN( 8,15,1); _FL_DEFPIN( 9,14,0); _FL_DEFPIN(10,15,0); _FL_DEFPIN(11,16,0);
_FL_DEFPIN(12,17,0); _FL_DEFPIN(13,18,0); _FL_DEFPIN(14,19,0); _FL_DEFPIN(15,16,1);
_FL_DEFPIN(16, 8,0); _FL_DEFPIN(17,28,0); _FL_DEFPIN(18,17,1); _FL_DEFPIN(19, 2,0);
_FL_DEFPIN(20, 6,0); _FL_DEFPIN(21, 5,0); _FL_DEFPIN(22, 4,0); _FL_DEFPIN(23, 9,1);
_FL_DEFPIN(24, 8,1); _FL_DEFPIN(25, 7,1); _FL_DEFPIN(26, 6,1); _FL_DEFPIN(27, 5,1);
_FL_DEFPIN(28, 4,1); _FL_DEFPIN(29, 7,0); _FL_DEFPIN(30, 3,1); _FL_DEFPIN(31, 2,1);
_FL_DEFPIN(32, 1,1); _FL_DEFPIN(33, 0,1); _FL_DEFPIN(34, 3,0); _FL_DEFPIN(35, 3,0);
_FL_DEFPIN(36,30,1); _FL_DEFPIN(37,31,1); _FL_DEFPIN(38,22,1); _FL_DEFPIN(39,23,1);
_FL_DEFPIN(40,12,0); _FL_DEFPIN(41,13,0); _FL_DEFPIN(42,22,0); _FL_DEFPIN(43,23,0);
_FL_DEFPIN(44,20,0); _FL_DEFPIN(45,21,0); _FL_DEFPIN(46,27,0); _FL_DEFPIN(47,24,0);
_FL_DEFPIN(48,25,0); _FL_DEFPIN(49,13,1); _FL_DEFPIN(50,14,1); _FL_DEFPIN(51,17,0);
_FL_DEFPIN(52,18,0); _FL_DEFPIN(53,12,1); _FL_DEFPIN(54,13,1); _FL_DEFPIN(55,14,1);
_FL_DEFPIN(56,15,1);
#define SPI_DATA 44
#define SPI_CLOCK 45
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ARDUINO_SAMD_WINO)
#define MAX_PIN 22
_FL_DEFPIN( 0, 23, 0); _FL_DEFPIN( 1, 22, 0); _FL_DEFPIN( 2, 16, 0); _FL_DEFPIN( 3, 17, 0);
_FL_DEFPIN( 4, 18, 0); _FL_DEFPIN( 5, 19, 0); _FL_DEFPIN( 6, 24, 0); _FL_DEFPIN( 7, 25, 0);
_FL_DEFPIN( 8, 27, 0); _FL_DEFPIN( 9, 28, 0); _FL_DEFPIN( 10, 30, 0); _FL_DEFPIN( 11, 31, 0);
_FL_DEFPIN( 12, 15, 0); _FL_DEFPIN( 13, 14, 0); _FL_DEFPIN( 14, 2, 0); _FL_DEFPIN( 15, 3, 0);
_FL_DEFPIN( 16, 4, 0); _FL_DEFPIN( 17, 5, 0); _FL_DEFPIN( 18, 6, 0); _FL_DEFPIN( 19, 7, 0);
_FL_DEFPIN( 20, 8, 0); _FL_DEFPIN( 21, 9, 0); _FL_DEFPIN( 22, 10, 0); _FL_DEFPIN( 23, 11, 0);
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRWIFI1010)
#define MAX_PIN 22
_FL_DEFPIN( 0, 22, 0); _FL_DEFPIN( 1, 23, 0); _FL_DEFPIN( 2, 10, 0); _FL_DEFPIN( 3, 11, 0);
_FL_DEFPIN( 4, 10, 1); _FL_DEFPIN( 5, 11, 1); _FL_DEFPIN( 6, 20, 0); _FL_DEFPIN( 7, 21, 0);
_FL_DEFPIN( 8, 16, 0); _FL_DEFPIN( 9, 17, 0); _FL_DEFPIN( 10, 19, 0); _FL_DEFPIN( 11, 8, 0);
_FL_DEFPIN( 12, 9, 0); _FL_DEFPIN( 13, 23, 1); _FL_DEFPIN( 14, 22, 1); _FL_DEFPIN( 15, 2, 0);
_FL_DEFPIN( 16, 2, 1); _FL_DEFPIN( 17, 3, 1); _FL_DEFPIN( 18, 4, 0); _FL_DEFPIN( 19, 5, 0);
_FL_DEFPIN( 20, 6, 0); _FL_DEFPIN( 21, 7, 0);
#define SPI_DATA 8
#define SPI_CLOCK 9
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ARDUINO_SAMD_NANO_33_IOT)
#define MAX_PIN 26
_FL_DEFPIN( 0, 23, 1); _FL_DEFPIN( 1, 22, 1); _FL_DEFPIN( 2, 10, 1); _FL_DEFPIN( 3, 11, 1);
_FL_DEFPIN( 4, 7, 0); _FL_DEFPIN( 5, 5, 0); _FL_DEFPIN( 6, 4, 0); _FL_DEFPIN( 7, 6, 0);
_FL_DEFPIN( 8, 18, 0); _FL_DEFPIN( 9, 20, 0); _FL_DEFPIN( 10, 21, 0); _FL_DEFPIN( 11, 16, 0);
_FL_DEFPIN( 12, 19, 0); _FL_DEFPIN( 13, 17, 0); _FL_DEFPIN( 14, 2, 0); _FL_DEFPIN( 15, 2, 1);
_FL_DEFPIN( 16, 11, 1); _FL_DEFPIN( 17, 10, 0); _FL_DEFPIN( 18, 8, 1); _FL_DEFPIN( 19, 9, 1);
_FL_DEFPIN( 20, 9, 0); _FL_DEFPIN( 21, 3, 1); _FL_DEFPIN( 22, 12, 0); _FL_DEFPIN( 23, 13, 0);
_FL_DEFPIN( 24, 14, 0); _FL_DEFPIN( 25, 15, 0);
#define SPI_DATA 22
#define SPI_CLOCK 25
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ARDUINO_GEMMA_M0)
#define MAX_PIN 4
_FL_DEFPIN( 0, 4, 0); _FL_DEFPIN( 1, 2, 0); _FL_DEFPIN( 2, 5, 0);
_FL_DEFPIN( 3, 0, 0); _FL_DEFPIN( 4, 1, 0);
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ADAFRUIT_TRINKET_M0)
#define MAX_PIN 7
_FL_DEFPIN( 0, 8, 0); _FL_DEFPIN( 1, 2, 0); _FL_DEFPIN( 2, 9, 0);
_FL_DEFPIN( 3, 7, 0); _FL_DEFPIN( 4, 6, 0); _FL_DEFPIN( 7, 0, 0); _FL_DEFPIN( 8, 1, 0);
#define SPI_DATA 4
#define SPI_CLOCK 3
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ADAFRUIT_QTPY_M0)
#define MAX_PIN 10
_FL_DEFPIN( 0, 2, 0); _FL_DEFPIN( 1, 3, 0); _FL_DEFPIN( 2, 4, 0); _FL_DEFPIN( 3, 5, 0);
_FL_DEFPIN( 4, 16, 0); _FL_DEFPIN( 5, 17, 0); _FL_DEFPIN( 6, 6, 0); _FL_DEFPIN( 7, 7, 0);
_FL_DEFPIN( 8, 11, 0); _FL_DEFPIN( 9, 9, 0); _FL_DEFPIN( 10, 10, 0);
#define SPI_DATA 10
#define SPI_CLOCK 8
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ADAFRUIT_ITSYBITSY_M0)
#define MAX_PIN 16
_FL_DEFPIN( 2, 14, 0); _FL_DEFPIN( 3, 9, 0); _FL_DEFPIN( 4, 8, 0);
_FL_DEFPIN( 5, 15, 0); _FL_DEFPIN( 6, 20, 0); _FL_DEFPIN( 7, 21, 0);
_FL_DEFPIN( 8, 6, 0); _FL_DEFPIN( 9, 7, 0); _FL_DEFPIN( 10, 18, 0);
_FL_DEFPIN( 11, 16, 0); _FL_DEFPIN( 12, 19, 0); _FL_DEFPIN( 13, 17, 0);
_FL_DEFPIN( 29, 10, 0); // MOSI
_FL_DEFPIN( 30, 11, 0); // SCK
_FL_DEFPIN( 40, 0, 0); //APA102 Clock
_FL_DEFPIN( 41, 0, 1) //APA102 Data
#define SPI_DATA 29
#define SPI_CLOCK 30
#define HAS_HARDWARE_PIN_SUPPORT 1
#endif
#endif // FASTLED_FORCE_SOFTWARE_PINS
FASTLED_NAMESPACE_END
#endif // __INC_FASTPIN_ARM_SAM_H

View File

@@ -0,0 +1,26 @@
#ifndef __INC_LED_SYSDEFS_ARM_D21_H
#define __INC_LED_SYSDEFS_ARM_D21_H
#define FASTLED_ARM
#define FASTLED_ARM_M0_PLUS
#ifndef INTERRUPT_THRESHOLD
#define INTERRUPT_THRESHOLD 1
#endif
// Default to allowing interrupts
#ifndef FASTLED_ALLOW_INTERRUPTS
#define FASTLED_ALLOW_INTERRUPTS 1
#endif
#if FASTLED_ALLOW_INTERRUPTS == 1
#define FASTLED_ACCURATE_CLOCK
#endif
// reusing/abusing cli/sei defs for due
#define cli() __disable_irq();
#define sei() __enable_irq();
#endif

View File

@@ -0,0 +1,4 @@
FastLED updates for adafruit FEATHER M4 and fixes to ITSBITSY M4 compiles
SAMD51
only tested on FEATHER M4 with DOTSTAR and neopixel strips

View File

@@ -0,0 +1,128 @@
#ifndef __INC_CLOCKLESS_ARM_D51
#define __INC_CLOCKLESS_ARM_D51
FASTLED_NAMESPACE_BEGIN
// Definition for a single channel clockless controller for SAMD51
// See clockless.h for detailed info on how the template parameters are used.
#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
#define FASTLED_HAS_CLOCKLESS 1
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPin<DATA_PIN>::port_t data_t;
data_t mPinMask;
data_ptr_t mPort;
CMinWait<WAIT_TIME> mWait;
public:
virtual void init() {
FastPin<DATA_PIN>::setOutput();
mPinMask = FastPin<DATA_PIN>::mask();
mPort = FastPin<DATA_PIN>::port();
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mWait.wait();
if(!showRGBInternal(pixels)) {
sei(); delayMicroseconds(WAIT_TIME); cli();
showRGBInternal(pixels);
}
mWait.mark();
}
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();
cli();
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
while(pixels.has(1)) {
pixels.stepDithering();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
// if interrupts took longer than 45µs, punt on the current frame
if(ARM_DWT_CYCCNT > next_mark) {
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
}
hi = *port | FastPin<DATA_PIN>::mask();
lo = *port & ~FastPin<DATA_PIN>::mask();
#endif
// Write first byte, read next byte
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.loadAndScale1();
// Write second byte, read 3rd byte
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.loadAndScale2();
// Write third byte, read 1st byte of next pixel
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.advanceAndLoadAndScale0();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
};
sei();
return ARM_DWT_CYCCNT;
}
};
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,7 @@
#ifndef __INC_FASTLED_ARM_D51_H
#define __INC_FASTLED_ARM_D51_H
#include "fastpin_arm_d51.h"
#include "clockless_arm_d51.h"
#endif

View File

@@ -0,0 +1,138 @@
#ifndef __INC_FASTPIN_ARM_D51_H
#define __INC_FASTPIN_ARM_D51_H
FASTLED_NAMESPACE_BEGIN
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
#warning "Software pin support forced, pin access will be slightly slower."
#define NO_HARDWARE_PIN_SUPPORT
#undef HAS_HARDWARE_PIN_SUPPORT
#else
/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
/// The registers are data output, set output, clear output, toggle output, input, and direction
template<uint8_t PIN, uint8_t _BIT, uint32_t _MASK, int _GRP> class _ARMPIN {
public:
typedef volatile uint32_t * port_ptr_t;
typedef uint32_t port_t;
#if 0
inline static void setOutput() {
if(_BIT<8) {
_CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4));
} else {
_CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4));
}
}
inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
#endif
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
inline static void hi() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTSET.reg = _MASK; }
inline static void lo() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTCLR.reg = _MASK; }
inline static void set(register port_t val) __attribute__ ((always_inline)) { PORT->Group[_GRP].OUT.reg = val; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline static void toggle() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTTGL.reg = _MASK; }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
inline static port_t hival() __attribute__ ((always_inline)) { return PORT->Group[_GRP].OUT.reg | _MASK; }
inline static port_t loval() __attribute__ ((always_inline)) { return PORT->Group[_GRP].OUT.reg & ~_MASK; }
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUT.reg; }
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUTSET.reg; }
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUTCLR.reg; }
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
};
#define _R(T) struct __gen_struct_ ## T
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline volatile PortGroup * r() { return T; } };
#define _FL_IO(L) _RD32(GPIO ## L)
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1 << BIT, L> {};
// Actual pin definitions
#if defined(ADAFRUIT_ITSYBITSY_M4_EXPRESS)
#define MAX_PIN 19
// D0-D13, including D6+D8 (DotStar CLK + DATA)
_FL_DEFPIN( 0, 16, 0); _FL_DEFPIN( 1, 17, 0); _FL_DEFPIN( 2, 7, 0); _FL_DEFPIN( 3, 22, 1);
_FL_DEFPIN( 4, 14, 0); _FL_DEFPIN( 5, 15, 0); _FL_DEFPIN( 6, 2, 1); _FL_DEFPIN( 7, 18, 0);
_FL_DEFPIN( 8, 3, 1); _FL_DEFPIN( 9, 19, 0); _FL_DEFPIN(10, 20, 0); _FL_DEFPIN(11, 21, 0);
_FL_DEFPIN(12, 23, 0); _FL_DEFPIN(13, 22, 0);
// A0-A5
_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 5, 0); _FL_DEFPIN(16, 8, 1); _FL_DEFPIN(17, 9, 1);
_FL_DEFPIN(18, 4, 0); _FL_DEFPIN(19, 6, 0); /* A6 is present in variant.h but couldn't find it on the schematic */
// SDA/SCL
_FL_DEFPIN(21, 12, 0); _FL_DEFPIN(22, 13, 0);
// 23..25 MISO/SCK/MOSI
_FL_DEFPIN(23, 23, 1); _FL_DEFPIN(24, 1, 0); _FL_DEFPIN(25, 0, 0);
#define SPI_DATA 25
#define SPI_CLOCK 24
#define HAS_HARDWARE_PIN_SUPPORT 1
// Actual pin definitions
#elif defined(ADAFRUIT_METRO_M4_AIRLIFT_LITE)
#define MAX_PIN 20
// D0-D13, including D6+D8 (DotStar CLK + DATA)
_FL_DEFPIN( 0, 23, 0); _FL_DEFPIN( 1, 22, 0); _FL_DEFPIN( 2, 17, 1); _FL_DEFPIN( 3, 16, 1);
_FL_DEFPIN( 4, 13, 1); _FL_DEFPIN( 5, 14, 1); _FL_DEFPIN( 6, 15, 1); _FL_DEFPIN( 7, 12, 1);
_FL_DEFPIN( 8, 21, 0); _FL_DEFPIN( 9, 20, 0); _FL_DEFPIN(10, 18, 0); _FL_DEFPIN(11, 19, 0);
_FL_DEFPIN(12, 17, 0); _FL_DEFPIN(13, 16, 0);
// A0-A5
_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 5, 0); _FL_DEFPIN(16, 6, 0); _FL_DEFPIN(17, 0, 1);
_FL_DEFPIN(18, 8, 1); _FL_DEFPIN(19, 9, 1);
// SDA/SCL
_FL_DEFPIN(22, 2, 1); _FL_DEFPIN(23, 3, 1);
// 23..25 MISO/SCK/MOSI
_FL_DEFPIN(24, 14, 0); _FL_DEFPIN(25, 13, 0); _FL_DEFPIN(26, 12, 0);
#define SPI_DATA 26
#define SPI_CLOCK 25
#define HAS_HARDWARE_PIN_SUPPORT 1
#elif defined(ADAFRUIT_FEATHER_M4_EXPRESS)
#define MAX_PIN 19
// D0-D13, including D8 (neopixel) no pins 2 3
_FL_DEFPIN( 0, 17, 1); _FL_DEFPIN( 1, 16, 1);
_FL_DEFPIN( 4, 14, 0); _FL_DEFPIN( 5, 16, 0); _FL_DEFPIN( 6, 18, 0);
_FL_DEFPIN( 8, 3, 1); _FL_DEFPIN( 9, 19, 0); _FL_DEFPIN(10, 20, 0); _FL_DEFPIN(11, 21, 0);
_FL_DEFPIN(12, 22, 0); _FL_DEFPIN(13, 23, 0);
// A0-A5
_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 5, 0); _FL_DEFPIN(16, 8, 1); _FL_DEFPIN(17, 9, 1);
_FL_DEFPIN(18, 4, 0); _FL_DEFPIN(19, 6, 0); /* A6 is present in variant.h but couldn't find it on the schematic */
// SDA/SCL
_FL_DEFPIN(21, 12, 0); _FL_DEFPIN(22, 13, 0);
// 23..25 MISO/MOSI/SCK
_FL_DEFPIN(23, 22, 1); _FL_DEFPIN(24, 23, 1); _FL_DEFPIN(25, 17, 0);
#define SPI_DATA 24
#define SPI_CLOCK 25
#define HAS_HARDWARE_PIN_SUPPORT 1
#endif
#endif // FASTLED_FORCE_SOFTWARE_PINS
FASTLED_NAMESPACE_END
#endif // __INC_FASTPIN_ARM_D51_H

View File

@@ -0,0 +1,27 @@
#ifndef __INC_LED_SYSDEFS_ARM_D51_H
#define __INC_LED_SYSDEFS_ARM_D51_H
#define FASTLED_ARM
// Note this is an M4, not an M0+, but this enables the shared m0clockless.h
#define FASTLED_ARM_M0_PLUS
#ifndef INTERRUPT_THRESHOLD
#define INTERRUPT_THRESHOLD 1
#endif
// Default to allowing interrupts
#ifndef FASTLED_ALLOW_INTERRUPTS
#define FASTLED_ALLOW_INTERRUPTS 0
#endif
#if FASTLED_ALLOW_INTERRUPTS == 1
#define FASTLED_ACCURATE_CLOCK
#endif
// reusing/abusing cli/sei defs for due
#define cli() __disable_irq();
#define sei() __enable_irq();
#endif

View File

@@ -0,0 +1,124 @@
#ifndef __INC_CLOCKLESS_ARM_K20_H
#define __INC_CLOCKLESS_ARM_K20_H
FASTLED_NAMESPACE_BEGIN
// Definition for a single channel clockless controller for the k20 family of chips, like that used in the teensy 3.0/3.1
// See clockless.h for detailed info on how the template parameters are used.
#if defined(FASTLED_TEENSY3)
#define FASTLED_HAS_CLOCKLESS 1
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPin<DATA_PIN>::port_t data_t;
data_t mPinMask;
data_ptr_t mPort;
CMinWait<WAIT_TIME> mWait;
public:
virtual void init() {
FastPin<DATA_PIN>::setOutput();
mPinMask = FastPin<DATA_PIN>::mask();
mPort = FastPin<DATA_PIN>::port();
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mWait.wait();
if(!showRGBInternal(pixels)) {
sei(); delayMicroseconds(WAIT_TIME); cli();
showRGBInternal(pixels);
}
mWait.mark();
}
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();
cli();
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
while(pixels.has(1)) {
pixels.stepDithering();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
// if interrupts took longer than 45µs, punt on the current frame
if(ARM_DWT_CYCCNT > next_mark) {
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
}
hi = *port | FastPin<DATA_PIN>::mask();
lo = *port & ~FastPin<DATA_PIN>::mask();
#endif
// Write first byte, read next byte
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.loadAndScale1();
// Write second byte, read 3rd byte
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.loadAndScale2();
// Write third byte, read 1st byte of next pixel
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.advanceAndLoadAndScale0();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
};
sei();
return ARM_DWT_CYCCNT;
}
};
#endif
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,332 @@
#ifndef __INC_BLOCK_CLOCKLESS_ARM_K20_H
#define __INC_BLOCK_CLOCKLESS_ARM_K20_H
// Definition for a single channel clockless controller for the k20 family of chips, like that used in the teensy 3.0/3.1
// See clockless.h for detailed info on how the template parameters are used.
#if defined(FASTLED_TEENSY3)
#define FASTLED_HAS_BLOCKLESS 1
#define PORTC_FIRST_PIN 15
#define PORTD_FIRST_PIN 2
#define HAS_PORTDC 1
#define PORT_MASK (((1<<LANES)-1) & ((FIRST_PIN==2) ? 0xFF : 0xFFF))
#define MIN(X,Y) (((X)<(Y)) ? (X):(Y))
#define USED_LANES ((FIRST_PIN==2) ? MIN(LANES,8) : MIN(LANES,12))
#include <kinetis.h>
FASTLED_NAMESPACE_BEGIN
template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 40>
class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PORT_MASK> {
typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPin<FIRST_PIN>::port_t data_t;
data_t mPinMask;
data_ptr_t mPort;
CMinWait<WAIT_TIME> mWait;
public:
virtual int size() { return CLEDController::size() * LANES; }
virtual void showPixels(PixelController<RGB_ORDER, LANES, PORT_MASK> & pixels) {
mWait.wait();
uint32_t clocks = showRGBInternal(pixels);
#if FASTLED_ALLOW_INTERRUPTS == 0
// Adjust the timer
long microsTaken = CLKS_TO_MICROS(clocks);
MS_COUNTER += (1 + (microsTaken / 1000));
#endif
mWait.mark();
}
virtual void init() {
if(FIRST_PIN == PORTC_FIRST_PIN) { // PORTC
switch(USED_LANES) {
case 12: FastPin<30>::setOutput();
case 11: FastPin<29>::setOutput();
case 10: FastPin<27>::setOutput();
case 9: FastPin<28>::setOutput();
case 8: FastPin<12>::setOutput();
case 7: FastPin<11>::setOutput();
case 6: FastPin<13>::setOutput();
case 5: FastPin<10>::setOutput();
case 4: FastPin<9>::setOutput();
case 3: FastPin<23>::setOutput();
case 2: FastPin<22>::setOutput();
case 1: FastPin<15>::setOutput();
}
} else if(FIRST_PIN == PORTD_FIRST_PIN) { // PORTD
switch(USED_LANES) {
case 8: FastPin<5>::setOutput();
case 7: FastPin<21>::setOutput();
case 6: FastPin<20>::setOutput();
case 5: FastPin<6>::setOutput();
case 4: FastPin<8>::setOutput();
case 3: FastPin<7>::setOutput();
case 2: FastPin<14>::setOutput();
case 1: FastPin<2>::setOutput();
}
}
mPinMask = FastPin<FIRST_PIN>::mask();
mPort = FastPin<FIRST_PIN>::port();
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
typedef union {
uint8_t bytes[12];
uint16_t shorts[6];
uint32_t raw[3];
} Lines;
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, PixelController<RGB_ORDER, LANES, PORT_MASK> &pixels) { // , register uint32_t & b2) {
register Lines b2;
if(USED_LANES>8) {
transpose8<1,2>(b.bytes,b2.bytes);
transpose8<1,2>(b.bytes+8,b2.bytes+1);
} else {
transpose8x1(b.bytes,b2.bytes);
}
register uint8_t d = pixels.template getd<PX>(pixels);
register uint8_t scale = pixels.template getscale<PX>(pixels);
for(register uint32_t i = 0; i < (USED_LANES/2); ++i) {
while(ARM_DWT_CYCCNT < next_mark);
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
if(USED_LANES>8) {
*FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
} else {
*FastPin<FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PORT_MASK);
}
while((next_mark - ARM_DWT_CYCCNT) > (T3));
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
b.bytes[i+(USED_LANES/2)] = pixels.template loadAndScale<PX>(pixels,i+(USED_LANES/2),d,scale);
}
// if folks use an odd numnber of lanes, get the last byte's value here
if(USED_LANES & 0x01) {
b.bytes[USED_LANES-1] = pixels.template loadAndScale<PX>(pixels,USED_LANES-1,d,scale);
}
for(register uint32_t i = USED_LANES/2; i < 8; ++i) {
while(ARM_DWT_CYCCNT < next_mark);
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
if(USED_LANES>8) {
*FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
} else {
// b2.bytes[0] = 0;
*FastPin<FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PORT_MASK);
}
while((next_mark - ARM_DWT_CYCCNT) > (T3));
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
}
}
// 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, LANES, PORT_MASK> &allpixels) {
// Get access to the clock
ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
ARM_DWT_CYCCNT = 0;
// Setup the pixel controller and load/scale the first byte
allpixels.preStepFirstByteDithering();
register Lines b0;
allpixels.preStepFirstByteDithering();
for(int i = 0; i < USED_LANES; ++i) {
b0.bytes[i] = allpixels.loadAndScale0(i);
}
cli();
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
while(allpixels.has(1)) {
#if (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
// if interrupts took longer than 45µs, punt on the current frame
if(ARM_DWT_CYCCNT > next_mark) {
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-5)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; }
}
#endif
allpixels.stepDithering();
// Write first byte, read next byte
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
// Write second byte, read 3rd byte
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
allpixels.advanceData();
// Write third byte
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
#if (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
};
return ARM_DWT_CYCCNT;
}
};
#define PMASK ((1<<(LANES))-1)
#define PMASK_HI (PMASK>>8 & 0xFF)
#define PMASK_LO (PMASK & 0xFF)
template <uint8_t LANES, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class SixteenWayInlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PMASK> {
typedef typename FastPin<PORTC_FIRST_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPin<PORTC_FIRST_PIN>::port_t data_t;
data_t mPinMask;
data_ptr_t mPort;
CMinWait<WAIT_TIME> mWait;
public:
virtual void init() {
static_assert(LANES <= 16, "Maximum of 16 lanes for Teensy parallel controllers!");
// FastPin<30>::setOutput();
// FastPin<29>::setOutput();
// FastPin<27>::setOutput();
// FastPin<28>::setOutput();
switch(LANES) {
case 16: FastPin<12>::setOutput();
case 15: FastPin<11>::setOutput();
case 14: FastPin<13>::setOutput();
case 13: FastPin<10>::setOutput();
case 12: FastPin<9>::setOutput();
case 11: FastPin<23>::setOutput();
case 10: FastPin<22>::setOutput();
case 9: FastPin<15>::setOutput();
case 8: FastPin<5>::setOutput();
case 7: FastPin<21>::setOutput();
case 6: FastPin<20>::setOutput();
case 5: FastPin<6>::setOutput();
case 4: FastPin<8>::setOutput();
case 3: FastPin<7>::setOutput();
case 2: FastPin<14>::setOutput();
case 1: FastPin<2>::setOutput();
}
}
virtual void showPixels(PixelController<RGB_ORDER, LANES, PMASK> & pixels) {
mWait.wait();
uint32_t clocks = showRGBInternal(pixels);
#if FASTLED_ALLOW_INTERRUPTS == 0
// Adjust the timer
long microsTaken = CLKS_TO_MICROS(clocks);
MS_COUNTER += (1 + (microsTaken / 1000));
#endif
mWait.mark();
}
typedef union {
uint8_t bytes[16];
uint16_t shorts[8];
uint32_t raw[4];
} Lines;
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, PixelController<RGB_ORDER,LANES, PMASK> &pixels) { // , register uint32_t & b2) {
register Lines b2;
transpose8x1(b.bytes,b2.bytes);
transpose8x1(b.bytes+8,b2.bytes+8);
register uint8_t d = pixels.template getd<PX>(pixels);
register uint8_t scale = pixels.template getscale<PX>(pixels);
for(register uint32_t i = 0; (i < LANES) && (i < 8); ++i) {
while(ARM_DWT_CYCCNT < next_mark);
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
*FastPin<PORTD_FIRST_PIN>::sport() = PMASK_LO;
*FastPin<PORTC_FIRST_PIN>::sport() = PMASK_HI;
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+6));
*FastPin<PORTD_FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PMASK_LO);
*FastPin<PORTC_FIRST_PIN>::cport() = ((~b2.bytes[15-i]) & PMASK_HI);
while((next_mark - ARM_DWT_CYCCNT) > (T3));
*FastPin<PORTD_FIRST_PIN>::cport() = PMASK_LO;
*FastPin<PORTC_FIRST_PIN>::cport() = PMASK_HI;
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
if(LANES==16 || (LANES>8 && ((i+8) < LANES))) {
b.bytes[i+8] = pixels.template loadAndScale<PX>(pixels,i+8,d,scale);
}
}
}
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
// gcc will use register Y for the this pointer.
static uint32_t showRGBInternal(PixelController<RGB_ORDER,LANES, PMASK> &allpixels) {
// Get access to the clock
ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
ARM_DWT_CYCCNT = 0;
// Setup the pixel controller and load/scale the first byte
allpixels.preStepFirstByteDithering();
register Lines b0;
allpixels.preStepFirstByteDithering();
for(int i = 0; i < LANES; ++i) {
b0.bytes[i] = allpixels.loadAndScale0(i);
}
cli();
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
while(allpixels.has(1)) {
allpixels.stepDithering();
#if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
// if interrupts took longer than 45µs, punt on the current frame
if(ARM_DWT_CYCCNT > next_mark) {
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; }
}
#endif
// Write first byte, read next byte
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
// Write second byte, read 3rd byte
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
allpixels.advanceData();
// Write third byte
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
#if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
};
sei();
return ARM_DWT_CYCCNT;
}
};
FASTLED_NAMESPACE_END
#endif
#endif

View File

@@ -0,0 +1,13 @@
#ifndef __INC_FASTLED_ARM_K20_H
#define __INC_FASTLED_ARM_K20_H
// Include the k20 headers
#include "fastpin_arm_k20.h"
#include "fastspi_arm_k20.h"
#include "octows2811_controller.h"
#include "ws2812serial_controller.h"
#include "smartmatrix_t3.h"
#include "clockless_arm_k20.h"
#include "clockless_block_arm_k20.h"
#endif

View File

@@ -0,0 +1,120 @@
#ifndef __FASTPIN_ARM_K20_H
#define __FASTPIN_ARM_K20_H
FASTLED_NAMESPACE_BEGIN
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
#warning "Software pin support forced, pin access will be sloightly slower."
#define NO_HARDWARE_PIN_SUPPORT
#undef HAS_HARDWARE_PIN_SUPPORT
#else
/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
/// The registers are data output, set output, clear output, toggle output, input, and direction
template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN {
public:
typedef volatile uint32_t * port_ptr_t;
typedef uint32_t port_t;
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
inline static void hi() __attribute__ ((always_inline)) { _PSOR::r() = _MASK; }
inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; }
inline static void set(register port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
};
/// 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 _R(T) struct __gen_struct_ ## T
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } \
template<int BIT> static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
#define _FL_IO(L,C) _RD32(GPIO ## L ## _PDOR); _RD32(GPIO ## L ## _PSOR); _RD32(GPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(GPIO ## L ## _PDIR); _RD32(GPIO ## L ## _PDDR); _FL_DEFINE_PORT3(L,C,_R(GPIO ## L ## _PDOR));
#define _FL_DEFPIN(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
_FL_IO(A,0); _FL_IO(B,1); _FL_IO(C,2); _FL_IO(D,3); _FL_IO(E,4);
#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
#define MAX_PIN 33
_FL_DEFPIN(0, 16, B); _FL_DEFPIN(1, 17, B); _FL_DEFPIN(2, 0, D); _FL_DEFPIN(3, 12, A);
_FL_DEFPIN(4, 13, A); _FL_DEFPIN(5, 7, D); _FL_DEFPIN(6, 4, D); _FL_DEFPIN(7, 2, D);
_FL_DEFPIN(8, 3, D); _FL_DEFPIN(9, 3, C); _FL_DEFPIN(10, 4, C); _FL_DEFPIN(11, 6, C);
_FL_DEFPIN(12, 7, C); _FL_DEFPIN(13, 5, C); _FL_DEFPIN(14, 1, D); _FL_DEFPIN(15, 0, C);
_FL_DEFPIN(16, 0, B); _FL_DEFPIN(17, 1, B); _FL_DEFPIN(18, 3, B); _FL_DEFPIN(19, 2, B);
_FL_DEFPIN(20, 5, D); _FL_DEFPIN(21, 6, D); _FL_DEFPIN(22, 1, C); _FL_DEFPIN(23, 2, C);
_FL_DEFPIN(24, 5, A); _FL_DEFPIN(25, 19, B); _FL_DEFPIN(26, 1, E); _FL_DEFPIN(27, 9, C);
_FL_DEFPIN(28, 8, C); _FL_DEFPIN(29, 10, C); _FL_DEFPIN(30, 11, C); _FL_DEFPIN(31, 0, E);
_FL_DEFPIN(32, 18, B); _FL_DEFPIN(33, 4, A);
#define SPI_DATA 11
#define SPI_CLOCK 13
#define SPI1 (*(SPI_t *)0x4002D000)
#define SPI2_DATA 7
#define SPI2_CLOCK 14
#define FASTLED_TEENSY3
#define ARM_HARDWARE_SPI
#define HAS_HARDWARE_PIN_SUPPORT
#endif
#endif // FASTLED_FORCE_SOFTWARE_PINS
FASTLED_NAMESPACE_END
#endif // __INC_FASTPIN_ARM_K20

View File

@@ -0,0 +1,466 @@
#ifndef __INC_FASTSPI_ARM_H
#define __INC_FASTSPI_ARM_H
FASTLED_NAMESPACE_BEGIN
#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 KINETISK_SPI0
#define KINETISK_SPI0 SPI0
#endif
#ifndef SPI_PUSHR_CONT
#define SPI_PUSHR_CONT SPIX.PUSHR_CONT
#define SPI_PUSHR_CTAS(X) SPIX.PUSHR_CTAS(X)
#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,
// 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> class BitWork<VAL, 0> {
public:
static int highestBit() __attribute__((always_inline)) { return 0; }
};
#define MAX(A, B) (( (A) > (B) ) ? (A) : (B))
#define USE_CONT 0
// intra-frame backup data
struct SPIState {
uint32_t _ctar0,_ctar1;
uint32_t pins[4];
};
// extern SPIState gState;
// Templated function to translate a clock divider value into the prescalar, scalar, and clock doubling setting for the world.
template <int VAL> void getScalars(uint32_t & preScalar, uint32_t & scalar, uint32_t & dbl) {
switch(VAL) {
// Handle the dbl clock cases
case 0: case 1:
case 2: preScalar = 0; scalar = 0; dbl = 1; break;
case 3: preScalar = 1; scalar = 0; dbl = 1; break;
case 5: preScalar = 2; scalar = 0; dbl = 1; break;
case 7: preScalar = 3; scalar = 0; dbl = 1; break;
// Handle the scalar value 6 cases (since it's not a power of two, it won't get caught
// below)
case 9: preScalar = 1; scalar = 2; dbl = 1; break;
case 18: case 19: preScalar = 1; scalar = 2; dbl = 0; break;
case 15: preScalar = 2; scalar = 2; dbl = 1; break;
case 30: case 31: preScalar = 2; scalar = 2; dbl = 0; break;
case 21: case 22: case 23: preScalar = 3; scalar = 2; dbl = 1; break;
case 42: case 43: case 44: case 45: case 46: case 47: preScalar = 3; scalar = 2; dbl = 0; break;
default: {
int p2 = BitWork<VAL/2, 15>::highestBit();
int p3 = BitWork<VAL/3, 15>::highestBit();
int p5 = BitWork<VAL/5, 15>::highestBit();
int p7 = BitWork<VAL/7, 15>::highestBit();
int w2 = 2 * (1 << p2);
int w3 = (VAL/3) > 0 ? 3 * (1 << p3) : 0;
int w5 = (VAL/5) > 0 ? 5 * (1 << p5) : 0;
int w7 = (VAL/7) > 0 ? 7 * (1 << p7) : 0;
int maxval = MAX(MAX(w2, w3), MAX(w5, w7));
if(w2 == maxval) { preScalar = 0; scalar = p2; }
else if(w3 == maxval) { preScalar = 1; scalar = p3; }
else if(w5 == maxval) { preScalar = 2; scalar = p5; }
else if(w7 == maxval) { preScalar = 3; scalar = p7; }
dbl = 0;
if(scalar == 0) { dbl = 1; }
else if(scalar < 3) { --scalar; }
}
}
return;
}
#define SPIX (*(SPI_t*)pSPIX)
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX>
class ARMHardwareSPIOutput {
Selectable *m_pSelect;
SPIState gState;
// Borrowed from the teensy3 SPSR emulation code -- note, enabling pin 7 disables pin 11 (and vice versa),
// and likewise enabling pin 14 disables pin 13 (and vice versa)
inline void enable_pins(void) __attribute__((always_inline)) {
//serial_print("enable_pins\n");
switch(_DATA_PIN) {
case 7:
CORE_PIN7_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
break;
case 11:
CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
break;
}
switch(_CLOCK_PIN) {
case 13:
CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
break;
case 14:
CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
break;
}
}
// Borrowed from the teensy3 SPSR emulation code. We disable the pins that we're using, and restore the state on the pins that we aren't using
inline void disable_pins(void) __attribute__((always_inline)) {
switch(_DATA_PIN) {
case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN11_CONFIG = gState.pins[1]; break;
case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN7_CONFIG = gState.pins[0]; break;
}
switch(_CLOCK_PIN) {
case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN14_CONFIG = gState.pins[3]; break;
case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN13_CONFIG = gState.pins[2]; break;
}
}
static inline void update_ctars(uint32_t ctar0, uint32_t ctar1) __attribute__((always_inline)) {
if(SPIX.CTAR0 == ctar0 && SPIX.CTAR1 == ctar1) return;
uint32_t mcr = SPIX.MCR;
if(mcr & SPI_MCR_MDIS) {
SPIX.CTAR0 = ctar0;
SPIX.CTAR1 = ctar1;
} else {
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
SPIX.CTAR0 = ctar0;
SPIX.CTAR1 = ctar1;
SPIX.MCR = mcr;
}
}
static inline void update_ctar0(uint32_t ctar) __attribute__((always_inline)) {
if (SPIX.CTAR0 == ctar) return;
uint32_t mcr = SPIX.MCR;
if (mcr & SPI_MCR_MDIS) {
SPIX.CTAR0 = ctar;
} else {
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
SPIX.CTAR0 = ctar;
SPIX.MCR = mcr;
}
}
static inline void update_ctar1(uint32_t ctar) __attribute__((always_inline)) {
if (SPIX.CTAR1 == ctar) return;
uint32_t mcr = SPIX.MCR;
if (mcr & SPI_MCR_MDIS) {
SPIX.CTAR1 = ctar;
} else {
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
SPIX.CTAR1 = ctar;
SPIX.MCR = mcr;
}
}
void setSPIRate() {
// Configure CTAR0, defaulting to 8 bits and CTAR1, defaulting to 16 bits
uint32_t _PBR = 0;
uint32_t _BR = 0;
uint32_t _CSSCK = 0;
uint32_t _DBR = 0;
// if(_SPI_CLOCK_DIVIDER >= 256) { _PBR = 0; _BR = _CSSCK = 7; _DBR = 0; } // osc/256
// else if(_SPI_CLOCK_DIVIDER >= 128) { _PBR = 0; _BR = _CSSCK = 6; _DBR = 0; } // osc/128
// else if(_SPI_CLOCK_DIVIDER >= 64) { _PBR = 0; _BR = _CSSCK = 5; _DBR = 0; } // osc/64
// else if(_SPI_CLOCK_DIVIDER >= 32) { _PBR = 0; _BR = _CSSCK = 4; _DBR = 0; } // osc/32
// else if(_SPI_CLOCK_DIVIDER >= 16) { _PBR = 0; _BR = _CSSCK = 3; _DBR = 0; } // osc/16
// else if(_SPI_CLOCK_DIVIDER >= 8) { _PBR = 0; _BR = _CSSCK = 1; _DBR = 0; } // osc/8
// else if(_SPI_CLOCK_DIVIDER >= 7) { _PBR = 3; _BR = _CSSCK = 0; _DBR = 1; } // osc/7
// else if(_SPI_CLOCK_DIVIDER >= 5) { _PBR = 2; _BR = _CSSCK = 0; _DBR = 1; } // osc/5
// else if(_SPI_CLOCK_DIVIDER >= 4) { _PBR = 0; _BR = _CSSCK = 0; _DBR = 0; } // osc/4
// else if(_SPI_CLOCK_DIVIDER >= 3) { _PBR = 1; _BR = _CSSCK = 0; _DBR = 1; } // osc/3
// else { _PBR = 0; _BR = _CSSCK = 0; _DBR = 1; } // osc/2
getScalars<_SPI_CLOCK_DIVIDER>(_PBR, _BR, _DBR);
_CSSCK = _BR;
uint32_t ctar0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
uint32_t ctar1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
#if USE_CONT == 1
ctar0 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
ctar1 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
#endif
if(_DBR) {
ctar0 |= SPI_CTAR_DBR;
ctar1 |= SPI_CTAR_DBR;
}
update_ctars(ctar0,ctar1);
}
void inline save_spi_state() __attribute__ ((always_inline)) {
// save ctar data
gState._ctar0 = SPIX.CTAR0;
gState._ctar1 = SPIX.CTAR1;
// save data for the not-us pins
gState.pins[0] = CORE_PIN7_CONFIG;
gState.pins[1] = CORE_PIN11_CONFIG;
gState.pins[2] = CORE_PIN13_CONFIG;
gState.pins[3] = CORE_PIN14_CONFIG;
}
void inline restore_spi_state() __attribute__ ((always_inline)) {
// restore ctar data
update_ctars(gState._ctar0,gState._ctar1);
// restore data for the not-us pins (not necessary because disable_pins will do this)
// CORE_PIN7_CONFIG = gState.pins[0];
// CORE_PIN11_CONFIG = gState.pins[1];
// CORE_PIN13_CONFIG = gState.pins[2];
// CORE_PIN14_CONFIG = gState.pins[3];
}
public:
ARMHardwareSPIOutput() { m_pSelect = NULL; }
ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
void init() {
// set the pins to output
FastPin<_DATA_PIN>::setOutput();
FastPin<_CLOCK_PIN>::setOutput();
// Enable SPI0 clock
uint32_t sim6 = SIM_SCGC6;
if((SPI_t*)pSPIX == &KINETISK_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);
}
}
// Configure SPI as the master and enable
SPIX.MCR |= SPI_MCR_MSTR; // | SPI_MCR_CONT_SCKE);
SPIX.MCR &= ~(SPI_MCR_MDIS | SPI_MCR_HALT);
// pin/spi configuration happens on select
}
static void waitFully() __attribute__((always_inline)) {
// Wait for the last byte to get shifted into the register
bool empty = false;
do {
cli();
if ((SPIX.SR & 0xF000) > 0) {
// reset the TCF flag
SPIX.SR |= SPI_SR_TCF;
} else {
empty = true;
}
sei();
} while (!empty);
// wait for the TCF flag to get set
while (!(SPIX.SR & SPI_SR_TCF));
SPIX.SR |= (SPI_SR_TCF | SPI_SR_EOQF);
}
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 };
#if USE_CONT == 1
#define CM CONT
#else
#define CM NOCONT
#endif
#define WM PRE
template<ECont CONT_STATE, EWait WAIT_STATE, ELast LAST_STATE> class Write {
public:
static void writeWord(uint16_t w) __attribute__((always_inline)) {
if(WAIT_STATE == PRE) { wait(); }
cli();
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
SPI_PUSHR_CTAS(1) | (w & 0xFFFF);
SPIX.SR |= SPI_SR_TCF;
sei();
if(WAIT_STATE == POST) { wait(); }
}
static void writeByte(uint8_t b) __attribute__((always_inline)) {
if(WAIT_STATE == PRE) { wait(); }
cli();
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
SPI_PUSHR_CTAS(0) | (b & 0xFF);
SPIX.SR |= SPI_SR_TCF;
sei();
if(WAIT_STATE == POST) { wait(); }
}
};
static void writeWord(uint16_t w) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei(); }
static void writeWordNoWait(uint16_t w) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei(); }
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); }
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF);SPIX.SR |= SPI_SR_TCF; sei(); wait(); }
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); }
static void writeWordCont(uint16_t w) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei(); }
static void writeWordContNoWait(uint16_t w) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei();}
static void writeByteCont(uint8_t b) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); }
static void writeByteContPostWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); wait(); }
static void writeByteContNoWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); }
// 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 = SPIX.CTAR1;
// 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);
writeWord( (b & (1 << BIT)) != 0);
update_ctar1(ctar1_save);
}
void inline select() __attribute__((always_inline)) {
save_spi_state();
if(m_pSelect != NULL) { m_pSelect->select(); }
setSPIRate();
enable_pins();
}
void inline release() __attribute__((always_inline)) {
disable_pins();
if(m_pSelect != NULL) { m_pSelect->release(); }
restore_spi_state();
}
static void writeBytesValueRaw(uint8_t value, int len) {
while(len--) { Write<CM, WM, NOTLAST>::writeByte(value); }
}
void writeBytesValue(uint8_t value, int len) {
select();
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) {
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 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;
// 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
while(pixels.has(2)) {
// Load and write out the first two bytes
if(WM == NONE) { wait1(); }
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(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(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1()));
Write<CM, WM, NOTLAST>::writeByte(D::adjust(pixels.loadAndScale2()));
}
D::postBlock(len);
waitFully();
} 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(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);
}
release();
}
};
#endif
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,46 @@
#ifndef __INC_LED_SYSDEFS_ARM_K20_H
#define __INC_LED_SYSDEFS_ARM_K20_H
#define FASTLED_TEENSY3
#define FASTLED_ARM
#ifndef INTERRUPT_THRESHOLD
#define INTERRUPT_THRESHOLD 1
#endif
// Default to allowing interrupts
#ifndef FASTLED_ALLOW_INTERRUPTS
#define FASTLED_ALLOW_INTERRUPTS 1
#endif
#if FASTLED_ALLOW_INTERRUPTS == 1
#define FASTLED_ACCURATE_CLOCK
#endif
#if (F_CPU == 96000000)
#define CLK_DBL 1
#endif
// Get some system include files
#include <avr/io.h>
#include <avr/interrupt.h> // for cli/se definitions
// Define the register types
#if defined(ARDUINO) // && ARDUINO < 150
typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
#endif
extern volatile uint32_t systick_millis_count;
# define MS_COUNTER systick_millis_count
// Default to using PROGMEM, since TEENSY3 provides it
// even though all it does is ignore it. Just being
// conservative here in case TEENSY3 changes.
#ifndef FASTLED_USE_PROGMEM
#define FASTLED_USE_PROGMEM 1
#endif
#endif

View File

@@ -0,0 +1,66 @@
#ifndef __INC_OCTOWS2811_CONTROLLER_H
#define __INC_OCTOWS2811_CONTROLLER_H
#ifdef USE_OCTOWS2811
// #include "OctoWS2811.h"
FASTLED_NAMESPACE_BEGIN
template<EOrder RGB_ORDER = GRB, uint8_t CHIP = WS2811_800kHz>
class COctoWS2811Controller : public CPixelLEDController<RGB_ORDER, 8, 0xFF> {
OctoWS2811 *pocto;
uint8_t *drawbuffer,*framebuffer;
void _init(int nLeds) {
if(pocto == NULL) {
drawbuffer = (uint8_t*)malloc(nLeds * 8 * 3);
framebuffer = (uint8_t*)malloc(nLeds * 8 * 3);
// byte ordering is handled in show by the pixel controller
int config = WS2811_RGB;
config |= CHIP;
pocto = new OctoWS2811(nLeds, framebuffer, drawbuffer, config);
pocto->begin();
}
}
public:
COctoWS2811Controller() { pocto = NULL; }
virtual int size() { return CLEDController::size() * 8; }
virtual void init() { /* do nothing yet */ }
typedef union {
uint8_t bytes[8];
uint32_t raw[2];
} Lines;
virtual void showPixels(PixelController<RGB_ORDER, 8, 0xFF> & pixels) {
_init(pixels.size());
uint8_t *pData = drawbuffer;
while(pixels.has(1)) {
Lines b;
for(int i = 0; i < 8; ++i) { b.bytes[i] = pixels.loadAndScale0(i); }
transpose8x1_MSB(b.bytes,pData); pData += 8;
for(int i = 0; i < 8; ++i) { b.bytes[i] = pixels.loadAndScale1(i); }
transpose8x1_MSB(b.bytes,pData); pData += 8;
for(int i = 0; i < 8; ++i) { b.bytes[i] = pixels.loadAndScale2(i); }
transpose8x1_MSB(b.bytes,pData); pData += 8;
pixels.stepDithering();
pixels.advanceData();
}
pocto->show();
}
};
FASTLED_NAMESPACE_END
#endif
#endif

View File

@@ -0,0 +1,54 @@
#ifndef __INC_SMARTMATRIX_T3_H
#define __INC_SMARTMATRIX_T3_H
#ifdef SmartMatrix_h
#include <SmartMatrix.h>
FASTLED_NAMESPACE_BEGIN
extern SmartMatrix *pSmartMatrix;
// note - dmx simple must be included before FastSPI for this code to be enabled
class CSmartMatrixController : public CPixelLEDController<RGB_ORDER> {
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;
}
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
if(SMART_MATRIX_CAN_TRIPLE_BUFFER) {
rgb24 *md = matrix.getRealBackBuffer();
} else {
rgb24 *md = matrix.backBuffer();
}
while(pixels.has(1)) {
md->red = pixels.loadAndScale0();
md->green = pixels.loadAndScale1();
md->blue = pixels.loadAndScale2();
md++;
pixels.advanceData();
pixels.stepDithering();
}
matrix.swapBuffers();
if(SMART_MATRIX_CAN_TRIPLE_BUFFER && pixels.advanceBy() > 0) {
matrix.setBackBuffer(pixels.mData);
}
}
};
FASTLED_NAMESPACE_END
#endif
#endif

View File

@@ -0,0 +1,47 @@
#ifndef __INC_WS2812SERIAL_CONTROLLER_H
#define __INC_WS2812SERIAL_CONTROLLER_H
#ifdef USE_WS2812SERIAL
FASTLED_NAMESPACE_BEGIN
template<int DATA_PIN, EOrder RGB_ORDER>
class CWS2812SerialController : public CPixelLEDController<RGB_ORDER, 8, 0xFF> {
WS2812Serial *pserial;
uint8_t *drawbuffer,*framebuffer;
void _init(int nLeds) {
if (pserial == NULL) {
drawbuffer = (uint8_t*)malloc(nLeds * 3);
framebuffer = (uint8_t*)malloc(nLeds * 12);
pserial = new WS2812Serial(nLeds, framebuffer, drawbuffer, DATA_PIN, WS2812_RGB);
pserial->begin();
}
}
public:
CWS2812SerialController() { pserial = NULL; }
virtual void init() { /* do nothing yet */ }
virtual void showPixels(PixelController<RGB_ORDER, 8, 0xFF> & pixels) {
_init(pixels.size());
uint8_t *p = drawbuffer;
while(pixels.has(1)) {
*p++ = pixels.loadAndScale0();
*p++ = pixels.loadAndScale1();
*p++ = pixels.loadAndScale2();
pixels.stepDithering();
pixels.advanceData();
}
pserial->show();
}
};
FASTLED_NAMESPACE_END
#endif // USE_WS2812SERIAL
#endif // __INC_WS2812SERIAL_CONTROLLER_H

View File

@@ -0,0 +1,124 @@
#ifndef __INC_CLOCKLESS_ARM_K66_H
#define __INC_CLOCKLESS_ARM_K66_H
FASTLED_NAMESPACE_BEGIN
// Definition for a single channel clockless controller for the k66 family of chips, like that used in the teensy 3.6
// See clockless.h for detailed info on how the template parameters are used.
#if defined(FASTLED_TEENSY3)
#define FASTLED_HAS_CLOCKLESS 1
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPin<DATA_PIN>::port_t data_t;
data_t mPinMask;
data_ptr_t mPort;
CMinWait<WAIT_TIME> mWait;
public:
virtual void init() {
FastPin<DATA_PIN>::setOutput();
mPinMask = FastPin<DATA_PIN>::mask();
mPort = FastPin<DATA_PIN>::port();
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mWait.wait();
if(!showRGBInternal(pixels)) {
sei(); delayMicroseconds(WAIT_TIME); cli();
showRGBInternal(pixels);
}
mWait.mark();
}
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();
cli();
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
while(pixels.has(1)) {
pixels.stepDithering();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
// if interrupts took longer than 45µs, punt on the current frame
if(ARM_DWT_CYCCNT > next_mark) {
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
}
hi = *port | FastPin<DATA_PIN>::mask();
lo = *port & ~FastPin<DATA_PIN>::mask();
#endif
// Write first byte, read next byte
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.loadAndScale1();
// Write second byte, read 3rd byte
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.loadAndScale2();
// Write third byte, read 1st byte of next pixel
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.advanceAndLoadAndScale0();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
};
sei();
return ARM_DWT_CYCCNT;
}
};
#endif
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,347 @@
#ifndef __INC_BLOCK_CLOCKLESS_ARM_K66_H
#define __INC_BLOCK_CLOCKLESS_ARM_K66_H
// Definition for a single channel clockless controller for the k66 family of chips, like that used in the teensy 3.6
// See clockless.h for detailed info on how the template parameters are used.
#if defined(FASTLED_TEENSY3)
#define FASTLED_HAS_BLOCKLESS 1
#define PORTB_FIRST_PIN 0
#define PORTC_FIRST_PIN 15
#define PORTD_FIRST_PIN 2
#define HAS_PORTDC 1
#define LANE_MASK (((1<<LANES)-1) & ((FIRST_PIN==2) ? 0xFF : 0xFFF))
#define PORT_SHIFT(P) ((P) << ((FIRST_PIN==0) ? 16 : 0))
#define PORT_MASK PORT_SHIFT(LANE_MASK)
#define MIN(X,Y) (((X)<(Y)) ? (X):(Y))
#define USED_LANES ((FIRST_PIN!=15) ? MIN(LANES,8) : MIN(LANES,12))
#include <kinetis.h>
FASTLED_NAMESPACE_BEGIN
template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 40>
class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, LANE_MASK> {
typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPin<FIRST_PIN>::port_t data_t;
data_t mPinMask;
data_ptr_t mPort;
CMinWait<WAIT_TIME> mWait;
public:
virtual int size() { return CLEDController::size() * LANES; }
virtual void showPixels(PixelController<RGB_ORDER, LANES, LANE_MASK> & pixels) {
mWait.wait();
uint32_t clocks = showRGBInternal(pixels);
#if FASTLED_ALLOW_INTERRUPTS == 0
// Adjust the timer
long microsTaken = CLKS_TO_MICROS(clocks);
MS_COUNTER += (1 + (microsTaken / 1000));
#endif
mWait.mark();
}
virtual void init() {
if(FIRST_PIN == PORTC_FIRST_PIN) { // PORTC
switch(USED_LANES) {
case 12: FastPin<30>::setOutput();
case 11: FastPin<29>::setOutput();
case 10: FastPin<27>::setOutput();
case 9: FastPin<28>::setOutput();
case 8: FastPin<12>::setOutput();
case 7: FastPin<11>::setOutput();
case 6: FastPin<13>::setOutput();
case 5: FastPin<10>::setOutput();
case 4: FastPin<9>::setOutput();
case 3: FastPin<23>::setOutput();
case 2: FastPin<22>::setOutput();
case 1: FastPin<15>::setOutput();
}
} else if(FIRST_PIN == PORTD_FIRST_PIN) { // PORTD
switch(USED_LANES) {
case 8: FastPin<5>::setOutput();
case 7: FastPin<21>::setOutput();
case 6: FastPin<20>::setOutput();
case 5: FastPin<6>::setOutput();
case 4: FastPin<8>::setOutput();
case 3: FastPin<7>::setOutput();
case 2: FastPin<14>::setOutput();
case 1: FastPin<2>::setOutput();
}
} else if (FIRST_PIN == PORTB_FIRST_PIN) { // PORTB
switch (USED_LANES) {
case 8: FastPin<45>::setOutput();
case 7: FastPin<44>::setOutput();
case 6: FastPin<46>::setOutput();
case 5: FastPin<43>::setOutput();
case 4: FastPin<30>::setOutput();
case 3: FastPin<29>::setOutput();
case 2: FastPin<1>::setOutput();
case 1: FastPin<0>::setOutput();
}
}
mPinMask = FastPin<FIRST_PIN>::mask();
mPort = FastPin<FIRST_PIN>::port();
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
typedef union {
uint8_t bytes[12];
uint16_t shorts[6];
uint32_t raw[3];
} Lines;
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, PixelController<RGB_ORDER, LANES, LANE_MASK> &pixels) { // , register uint32_t & b2) {
register Lines b2;
if(USED_LANES>8) {
transpose8<1,2>(b.bytes,b2.bytes);
transpose8<1,2>(b.bytes+8,b2.bytes+1);
} else {
transpose8x1(b.bytes,b2.bytes);
}
register uint8_t d = pixels.template getd<PX>(pixels);
register uint8_t scale = pixels.template getscale<PX>(pixels);
for(register uint32_t i = 0; i < (USED_LANES/2); ++i) {
while(ARM_DWT_CYCCNT < next_mark);
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
if(USED_LANES>8) {
*FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
} else {
*FastPin<FIRST_PIN>::cport() = (PORT_SHIFT(~b2.bytes[7-i]) & PORT_MASK);
}
while((next_mark - ARM_DWT_CYCCNT) > (T3));
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
b.bytes[i+(USED_LANES/2)] = pixels.template loadAndScale<PX>(pixels,i+(USED_LANES/2),d,scale);
}
// if folks use an odd numnber of lanes, get the last byte's value here
if(USED_LANES & 0x01) {
b.bytes[USED_LANES-1] = pixels.template loadAndScale<PX>(pixels,USED_LANES-1,d,scale);
}
for(register uint32_t i = USED_LANES/2; i < 8; ++i) {
while(ARM_DWT_CYCCNT < next_mark);
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
if(USED_LANES>8) {
*FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
} else {
// b2.bytes[0] = 0;
*FastPin<FIRST_PIN>::cport() = (PORT_SHIFT(~b2.bytes[7-i]) & PORT_MASK);
}
while((next_mark - ARM_DWT_CYCCNT) > (T3));
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
}
}
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
// gcc will use register Y for the this pointer.
static uint32_t showRGBInternal(PixelController<RGB_ORDER, LANES, LANE_MASK> &allpixels) {
// Get access to the clock
ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
ARM_DWT_CYCCNT = 0;
// Setup the pixel controller and load/scale the first byte
allpixels.preStepFirstByteDithering();
register Lines b0;
allpixels.preStepFirstByteDithering();
for(int i = 0; i < USED_LANES; ++i) {
b0.bytes[i] = allpixels.loadAndScale0(i);
}
cli();
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
while(allpixels.has(1)) {
#if (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
// if interrupts took longer than 45µs, punt on the current frame
if(ARM_DWT_CYCCNT > next_mark) {
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-5)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; }
}
#endif
allpixels.stepDithering();
// Write first byte, read next byte
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
// Write second byte, read 3rd byte
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
allpixels.advanceData();
// Write third byte
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
#if (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
};
return ARM_DWT_CYCCNT;
}
};
#define PMASK ((1<<(LANES))-1)
#define PMASK_HI (PMASK>>8 & 0xFF)
#define PMASK_LO (PMASK & 0xFF)
template <uint8_t LANES, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class SixteenWayInlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PMASK> {
typedef typename FastPin<PORTC_FIRST_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPin<PORTC_FIRST_PIN>::port_t data_t;
data_t mPinMask;
data_ptr_t mPort;
CMinWait<WAIT_TIME> mWait;
public:
virtual void init() {
static_assert(LANES <= 16, "Maximum of 16 lanes for Teensy parallel controllers!");
// FastPin<30>::setOutput();
// FastPin<29>::setOutput();
// FastPin<27>::setOutput();
// FastPin<28>::setOutput();
switch(LANES) {
case 16: FastPin<12>::setOutput();
case 15: FastPin<11>::setOutput();
case 14: FastPin<13>::setOutput();
case 13: FastPin<10>::setOutput();
case 12: FastPin<9>::setOutput();
case 11: FastPin<23>::setOutput();
case 10: FastPin<22>::setOutput();
case 9: FastPin<15>::setOutput();
case 8: FastPin<5>::setOutput();
case 7: FastPin<21>::setOutput();
case 6: FastPin<20>::setOutput();
case 5: FastPin<6>::setOutput();
case 4: FastPin<8>::setOutput();
case 3: FastPin<7>::setOutput();
case 2: FastPin<14>::setOutput();
case 1: FastPin<2>::setOutput();
}
}
virtual void showPixels(PixelController<RGB_ORDER, LANES, PMASK> & pixels) {
mWait.wait();
uint32_t clocks = showRGBInternal(pixels);
#if FASTLED_ALLOW_INTERRUPTS == 0
// Adjust the timer
long microsTaken = CLKS_TO_MICROS(clocks);
MS_COUNTER += (1 + (microsTaken / 1000));
#endif
mWait.mark();
}
typedef union {
uint8_t bytes[16];
uint16_t shorts[8];
uint32_t raw[4];
} Lines;
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, PixelController<RGB_ORDER,LANES, PMASK> &pixels) { // , register uint32_t & b2) {
register Lines b2;
transpose8x1(b.bytes,b2.bytes);
transpose8x1(b.bytes+8,b2.bytes+8);
register uint8_t d = pixels.template getd<PX>(pixels);
register uint8_t scale = pixels.template getscale<PX>(pixels);
for(register uint32_t i = 0; (i < LANES) && (i < 8); ++i) {
while(ARM_DWT_CYCCNT < next_mark);
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
*FastPin<PORTD_FIRST_PIN>::sport() = PMASK_LO;
*FastPin<PORTC_FIRST_PIN>::sport() = PMASK_HI;
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+6));
*FastPin<PORTD_FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PMASK_LO);
*FastPin<PORTC_FIRST_PIN>::cport() = ((~b2.bytes[15-i]) & PMASK_HI);
while((next_mark - ARM_DWT_CYCCNT) > (T3));
*FastPin<PORTD_FIRST_PIN>::cport() = PMASK_LO;
*FastPin<PORTC_FIRST_PIN>::cport() = PMASK_HI;
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
if(LANES==16 || (LANES>8 && ((i+8) < LANES))) {
b.bytes[i+8] = pixels.template loadAndScale<PX>(pixels,i+8,d,scale);
}
}
}
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
// gcc will use register Y for the this pointer.
static uint32_t showRGBInternal(PixelController<RGB_ORDER,LANES, PMASK> &allpixels) {
// Get access to the clock
ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
ARM_DWT_CYCCNT = 0;
// Setup the pixel controller and load/scale the first byte
allpixels.preStepFirstByteDithering();
register Lines b0;
allpixels.preStepFirstByteDithering();
for(int i = 0; i < LANES; ++i) {
b0.bytes[i] = allpixels.loadAndScale0(i);
}
cli();
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
while(allpixels.has(1)) {
allpixels.stepDithering();
#if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
// if interrupts took longer than 45µs, punt on the current frame
if(ARM_DWT_CYCCNT > next_mark) {
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) {
sei();
return ARM_DWT_CYCCNT; }
}
#endif
// Write first byte, read next byte
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
// Write second byte, read 3rd byte
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
allpixels.advanceData();
// Write third byte
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
#if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
};
sei();
return ARM_DWT_CYCCNT;
}
};
FASTLED_NAMESPACE_END
#endif
#endif

View File

@@ -0,0 +1,14 @@
#ifndef __INC_FASTLED_ARM_K66_H
#define __INC_FASTLED_ARM_K66_H
// Include the k66 headers
#include "fastpin_arm_k66.h"
#include "fastspi_arm_k66.h"
#include "../k20/octows2811_controller.h"
#include "../k20/ws2812serial_controller.h"
#include "../k20/smartmatrix_t3.h"
#include "clockless_arm_k66.h"
#include "clockless_block_arm_k66.h"
#endif

View File

@@ -0,0 +1,128 @@
#ifndef __FASTPIN_ARM_K66_H
#define __FASTPIN_ARM_K66_H
FASTLED_NAMESPACE_BEGIN
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
#warning "Software pin support forced, pin access will be slightly slower."
#define NO_HARDWARE_PIN_SUPPORT
#undef HAS_HARDWARE_PIN_SUPPORT
#else
/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
/// The registers are data output, set output, clear output, toggle output, input, and direction
template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN {
public:
typedef volatile uint32_t * port_ptr_t;
typedef uint32_t port_t;
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
inline static void hi() __attribute__ ((always_inline)) { _PSOR::r() = _MASK; }
inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; }
inline static void set(register port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
};
/// 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 _R(T) struct __gen_struct_ ## T
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } \
template<int BIT> static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
#define _FL_IO(L,C) _RD32(GPIO ## L ## _PDOR); _RD32(GPIO ## L ## _PSOR); _RD32(GPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(GPIO ## L ## _PDIR); _RD32(GPIO ## L ## _PDDR); _FL_DEFINE_PORT3(L,C,_R(GPIO ## L ## _PDOR));
#define _FL_DEFPIN(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)> {};
_FL_IO(A,0); _FL_IO(B,1); _FL_IO(C,2); _FL_IO(D,3); _FL_IO(E,4);
// Actual pin definitions
#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
#define MAX_PIN 63
_FL_DEFPIN( 0, 16, B); _FL_DEFPIN( 1, 17, B); _FL_DEFPIN( 2, 0, D); _FL_DEFPIN( 3, 12, A);
_FL_DEFPIN( 4, 13, A); _FL_DEFPIN( 5, 7, D); _FL_DEFPIN( 6, 4, D); _FL_DEFPIN( 7, 2, D);
_FL_DEFPIN( 8, 3, D); _FL_DEFPIN( 9, 3, C); _FL_DEFPIN(10, 4, C); _FL_DEFPIN(11, 6, C);
_FL_DEFPIN(12, 7, C); _FL_DEFPIN(13, 5, C); _FL_DEFPIN(14, 1, D); _FL_DEFPIN(15, 0, C);
_FL_DEFPIN(16, 0, B); _FL_DEFPIN(17, 1, B); _FL_DEFPIN(18, 3, B); _FL_DEFPIN(19, 2, B);
_FL_DEFPIN(20, 5, D); _FL_DEFPIN(21, 6, D); _FL_DEFPIN(22, 1, C); _FL_DEFPIN(23, 2, C);
_FL_DEFPIN(24, 26, E); _FL_DEFPIN(25, 5, A); _FL_DEFPIN(26, 14, A); _FL_DEFPIN(27, 15, A);
_FL_DEFPIN(28, 16, A); _FL_DEFPIN(29, 18, B); _FL_DEFPIN(30, 19, B); _FL_DEFPIN(31, 10, B);
_FL_DEFPIN(32, 11, B); _FL_DEFPIN(33, 24, E); _FL_DEFPIN(34, 25, E); _FL_DEFPIN(35, 8, C);
_FL_DEFPIN(36, 9, C); _FL_DEFPIN(37, 10, C); _FL_DEFPIN(38, 11, C); _FL_DEFPIN(39, 17, A);
_FL_DEFPIN(40, 28, A); _FL_DEFPIN(41, 29, A); _FL_DEFPIN(42, 26, A); _FL_DEFPIN(43, 20, B);
_FL_DEFPIN(44, 22, B); _FL_DEFPIN(45, 23, B); _FL_DEFPIN(46, 21, B); _FL_DEFPIN(47, 8, D);
_FL_DEFPIN(48, 9, D); _FL_DEFPIN(49, 4, B); _FL_DEFPIN(50, 5, B); _FL_DEFPIN(51, 14, D);
_FL_DEFPIN(52, 13, D); _FL_DEFPIN(53, 12, D); _FL_DEFPIN(54, 15, D); _FL_DEFPIN(55, 11, D);
_FL_DEFPIN(56, 10, E); _FL_DEFPIN(57, 11, E); _FL_DEFPIN(58, 0, E); _FL_DEFPIN(59, 1, E);
_FL_DEFPIN(60, 2, E); _FL_DEFPIN(61, 3, E); _FL_DEFPIN(62, 4, E); _FL_DEFPIN(63, 5, E);
#define SPI_DATA 11
#define SPI_CLOCK 13
#define SPI2_DATA 7
#define SPI2_CLOCK 14
#define FASTLED_TEENSY3
#define ARM_HARDWARE_SPI
#define HAS_HARDWARE_PIN_SUPPORT
#endif
#endif // FASTLED_FORCE_SOFTWARE_PINS
FASTLED_NAMESPACE_END
#endif // __INC_FASTPIN_ARM_K66

View File

@@ -0,0 +1,470 @@
#ifndef __INC_FASTSPI_ARM_H
#define __INC_FASTSPI_ARM_H
//
// copied from k20 code
// changed SPI1 define to KINETISK_SPI1
// TODO: add third alternative MOSI pin (28) and CLOCK pin (27)
// TODO: add alternative pins for SPI1
// TODO: add SPI2 output
//
FASTLED_NAMESPACE_BEGIN
#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 KINETISK_SPI0
#define KINETISK_SPI0 SPI0
#endif
#ifndef SPI_PUSHR_CONT
#define SPI_PUSHR_CONT SPIX.PUSHR_CONT
#define SPI_PUSHR_CTAS(X) SPIX.PUSHR_CTAS(X)
#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,
// 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> class BitWork<VAL, 0> {
public:
static int highestBit() __attribute__((always_inline)) { return 0; }
};
#define MAX(A, B) (( (A) > (B) ) ? (A) : (B))
#define USE_CONT 0
// intra-frame backup data
struct SPIState {
uint32_t _ctar0,_ctar1;
uint32_t pins[4];
};
// extern SPIState gState;
// Templated function to translate a clock divider value into the prescalar, scalar, and clock doubling setting for the world.
template <int VAL> void getScalars(uint32_t & preScalar, uint32_t & scalar, uint32_t & dbl) {
switch(VAL) {
// Handle the dbl clock cases
case 0: case 1:
case 2: preScalar = 0; scalar = 0; dbl = 1; break;
case 3: preScalar = 1; scalar = 0; dbl = 1; break;
case 5: preScalar = 2; scalar = 0; dbl = 1; break;
case 7: preScalar = 3; scalar = 0; dbl = 1; break;
// Handle the scalar value 6 cases (since it's not a power of two, it won't get caught
// below)
case 9: preScalar = 1; scalar = 2; dbl = 1; break;
case 18: case 19: preScalar = 1; scalar = 2; dbl = 0; break;
case 15: preScalar = 2; scalar = 2; dbl = 1; break;
case 30: case 31: preScalar = 2; scalar = 2; dbl = 0; break;
case 21: case 22: case 23: preScalar = 3; scalar = 2; dbl = 1; break;
case 42: case 43: case 44: case 45: case 46: case 47: preScalar = 3; scalar = 2; dbl = 0; break;
default: {
int p2 = BitWork<VAL/2, 15>::highestBit();
int p3 = BitWork<VAL/3, 15>::highestBit();
int p5 = BitWork<VAL/5, 15>::highestBit();
int p7 = BitWork<VAL/7, 15>::highestBit();
int w2 = 2 * (1 << p2);
int w3 = (VAL/3) > 0 ? 3 * (1 << p3) : 0;
int w5 = (VAL/5) > 0 ? 5 * (1 << p5) : 0;
int w7 = (VAL/7) > 0 ? 7 * (1 << p7) : 0;
int maxval = MAX(MAX(w2, w3), MAX(w5, w7));
if(w2 == maxval) { preScalar = 0; scalar = p2; }
else if(w3 == maxval) { preScalar = 1; scalar = p3; }
else if(w5 == maxval) { preScalar = 2; scalar = p5; }
else if(w7 == maxval) { preScalar = 3; scalar = p7; }
dbl = 0;
if(scalar == 0) { dbl = 1; }
else if(scalar < 3) { --scalar; }
}
}
return;
}
#define SPIX (*(SPI_t*)pSPIX)
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX>
class ARMHardwareSPIOutput {
Selectable *m_pSelect;
SPIState gState;
// Borrowed from the teensy3 SPSR emulation code -- note, enabling pin 7 disables pin 11 (and vice versa),
// and likewise enabling pin 14 disables pin 13 (and vice versa)
inline void enable_pins(void) __attribute__((always_inline)) {
//serial_print("enable_pins\n");
switch(_DATA_PIN) {
case 7:
CORE_PIN7_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
break;
case 11:
CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
break;
}
switch(_CLOCK_PIN) {
case 13:
CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
break;
case 14:
CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
break;
}
}
// Borrowed from the teensy3 SPSR emulation code. We disable the pins that we're using, and restore the state on the pins that we aren't using
inline void disable_pins(void) __attribute__((always_inline)) {
switch(_DATA_PIN) {
case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN11_CONFIG = gState.pins[1]; break;
case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN7_CONFIG = gState.pins[0]; break;
}
switch(_CLOCK_PIN) {
case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN14_CONFIG = gState.pins[3]; break;
case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN13_CONFIG = gState.pins[2]; break;
}
}
static inline void update_ctars(uint32_t ctar0, uint32_t ctar1) __attribute__((always_inline)) {
if(SPIX.CTAR0 == ctar0 && SPIX.CTAR1 == ctar1) return;
uint32_t mcr = SPIX.MCR;
if(mcr & SPI_MCR_MDIS) {
SPIX.CTAR0 = ctar0;
SPIX.CTAR1 = ctar1;
} else {
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
SPIX.CTAR0 = ctar0;
SPIX.CTAR1 = ctar1;
SPIX.MCR = mcr;
}
}
static inline void update_ctar0(uint32_t ctar) __attribute__((always_inline)) {
if (SPIX.CTAR0 == ctar) return;
uint32_t mcr = SPIX.MCR;
if (mcr & SPI_MCR_MDIS) {
SPIX.CTAR0 = ctar;
} else {
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
SPIX.CTAR0 = ctar;
SPIX.MCR = mcr;
}
}
static inline void update_ctar1(uint32_t ctar) __attribute__((always_inline)) {
if (SPIX.CTAR1 == ctar) return;
uint32_t mcr = SPIX.MCR;
if (mcr & SPI_MCR_MDIS) {
SPIX.CTAR1 = ctar;
} else {
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
SPIX.CTAR1 = ctar;
SPIX.MCR = mcr;
}
}
void setSPIRate() {
// Configure CTAR0, defaulting to 8 bits and CTAR1, defaulting to 16 bits
uint32_t _PBR = 0;
uint32_t _BR = 0;
uint32_t _CSSCK = 0;
uint32_t _DBR = 0;
// if(_SPI_CLOCK_DIVIDER >= 256) { _PBR = 0; _BR = _CSSCK = 7; _DBR = 0; } // osc/256
// else if(_SPI_CLOCK_DIVIDER >= 128) { _PBR = 0; _BR = _CSSCK = 6; _DBR = 0; } // osc/128
// else if(_SPI_CLOCK_DIVIDER >= 64) { _PBR = 0; _BR = _CSSCK = 5; _DBR = 0; } // osc/64
// else if(_SPI_CLOCK_DIVIDER >= 32) { _PBR = 0; _BR = _CSSCK = 4; _DBR = 0; } // osc/32
// else if(_SPI_CLOCK_DIVIDER >= 16) { _PBR = 0; _BR = _CSSCK = 3; _DBR = 0; } // osc/16
// else if(_SPI_CLOCK_DIVIDER >= 8) { _PBR = 0; _BR = _CSSCK = 1; _DBR = 0; } // osc/8
// else if(_SPI_CLOCK_DIVIDER >= 7) { _PBR = 3; _BR = _CSSCK = 0; _DBR = 1; } // osc/7
// else if(_SPI_CLOCK_DIVIDER >= 5) { _PBR = 2; _BR = _CSSCK = 0; _DBR = 1; } // osc/5
// else if(_SPI_CLOCK_DIVIDER >= 4) { _PBR = 0; _BR = _CSSCK = 0; _DBR = 0; } // osc/4
// else if(_SPI_CLOCK_DIVIDER >= 3) { _PBR = 1; _BR = _CSSCK = 0; _DBR = 1; } // osc/3
// else { _PBR = 0; _BR = _CSSCK = 0; _DBR = 1; } // osc/2
getScalars<_SPI_CLOCK_DIVIDER>(_PBR, _BR, _DBR);
_CSSCK = _BR;
uint32_t ctar0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
uint32_t ctar1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
#if USE_CONT == 1
ctar0 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
ctar1 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
#endif
if(_DBR) {
ctar0 |= SPI_CTAR_DBR;
ctar1 |= SPI_CTAR_DBR;
}
update_ctars(ctar0,ctar1);
}
void inline save_spi_state() __attribute__ ((always_inline)) {
// save ctar data
gState._ctar0 = SPIX.CTAR0;
gState._ctar1 = SPIX.CTAR1;
// save data for the not-us pins
gState.pins[0] = CORE_PIN7_CONFIG;
gState.pins[1] = CORE_PIN11_CONFIG;
gState.pins[2] = CORE_PIN13_CONFIG;
gState.pins[3] = CORE_PIN14_CONFIG;
}
void inline restore_spi_state() __attribute__ ((always_inline)) {
// restore ctar data
update_ctars(gState._ctar0,gState._ctar1);
// restore data for the not-us pins (not necessary because disable_pins will do this)
// CORE_PIN7_CONFIG = gState.pins[0];
// CORE_PIN11_CONFIG = gState.pins[1];
// CORE_PIN13_CONFIG = gState.pins[2];
// CORE_PIN14_CONFIG = gState.pins[3];
}
public:
ARMHardwareSPIOutput() { m_pSelect = NULL; }
ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
void init() {
// set the pins to output
FastPin<_DATA_PIN>::setOutput();
FastPin<_CLOCK_PIN>::setOutput();
// Enable SPI0 clock
uint32_t sim6 = SIM_SCGC6;
if((SPI_t*)pSPIX == &KINETISK_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 == &KINETISK_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);
}
}
// Configure SPI as the master and enable
SPIX.MCR |= SPI_MCR_MSTR; // | SPI_MCR_CONT_SCKE);
SPIX.MCR &= ~(SPI_MCR_MDIS | SPI_MCR_HALT);
// pin/spi configuration happens on select
}
static void waitFully() __attribute__((always_inline)) {
// Wait for the last byte to get shifted into the register
bool empty = false;
do {
cli();
if ((SPIX.SR & 0xF000) > 0) {
// reset the TCF flag
SPIX.SR |= SPI_SR_TCF;
} else {
empty = true;
}
sei();
} while (!empty);
// wait for the TCF flag to get set
while (!(SPIX.SR & SPI_SR_TCF));
SPIX.SR |= (SPI_SR_TCF | SPI_SR_EOQF);
}
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 };
#if USE_CONT == 1
#define CM CONT
#else
#define CM NOCONT
#endif
#define WM PRE
template<ECont CONT_STATE, EWait WAIT_STATE, ELast LAST_STATE> class Write {
public:
static void writeWord(uint16_t w) __attribute__((always_inline)) {
if(WAIT_STATE == PRE) { wait(); }
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
SPI_PUSHR_CTAS(1) | (w & 0xFFFF);
SPIX.SR |= SPI_SR_TCF;
if(WAIT_STATE == POST) { wait(); }
}
static void writeByte(uint8_t b) __attribute__((always_inline)) {
if(WAIT_STATE == PRE) { wait(); }
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
SPI_PUSHR_CTAS(0) | (b & 0xFF);
SPIX.SR |= SPI_SR_TCF;
if(WAIT_STATE == POST) { wait(); }
}
};
static void writeWord(uint16_t w) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;}
static void writeWordNoWait(uint16_t w) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;}
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;}
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF);SPIX.SR |= SPI_SR_TCF; wait(); }
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;}
static void writeWordCont(uint16_t w) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;}
static void writeWordContNoWait(uint16_t w) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;}
static void writeByteCont(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;}
static void writeByteContPostWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;wait(); }
static void writeByteContNoWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;}
// 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 = SPIX.CTAR1;
// 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);
writeWord( (b & (1 << BIT)) != 0);
update_ctar1(ctar1_save);
}
void inline select() __attribute__((always_inline)) {
save_spi_state();
if(m_pSelect != NULL) { m_pSelect->select(); }
setSPIRate();
enable_pins();
}
void inline release() __attribute__((always_inline)) {
disable_pins();
if(m_pSelect != NULL) { m_pSelect->release(); }
restore_spi_state();
}
static void writeBytesValueRaw(uint8_t value, int len) {
while(len--) { Write<CM, WM, NOTLAST>::writeByte(value); }
}
void writeBytesValue(uint8_t value, int len) {
select();
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) {
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 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;
// 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
while(pixels.has(2)) {
// Load and write out the first two bytes
if(WM == NONE) { wait1(); }
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(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(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1()));
Write<CM, WM, NOTLAST>::writeByte(D::adjust(pixels.loadAndScale2()));
}
D::postBlock(len);
waitFully();
} 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(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);
}
release();
}
};
#endif
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,46 @@
#ifndef __INC_LED_SYSDEFS_ARM_K66_H
#define __INC_LED_SYSDEFS_ARM_K66_H
#define FASTLED_TEENSY3
#define FASTLED_ARM
#ifndef INTERRUPT_THRESHOLD
#define INTERRUPT_THRESHOLD 1
#endif
// Default to allowing interrupts
#ifndef FASTLED_ALLOW_INTERRUPTS
#define FASTLED_ALLOW_INTERRUPTS 1
#endif
#if FASTLED_ALLOW_INTERRUPTS == 1
#define FASTLED_ACCURATE_CLOCK
#endif
#if (F_CPU == 192000000)
#define CLK_DBL 1
#endif
// Get some system include files
#include <avr/io.h>
#include <avr/interrupt.h> // for cli/se definitions
// Define the register types
#if defined(ARDUINO) // && ARDUINO < 150
typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
#endif
extern volatile uint32_t systick_millis_count;
# define MS_COUNTER systick_millis_count
// Default to using PROGMEM, since TEENSY3 provides it
// even though all it does is ignore it. Just being
// conservative here in case TEENSY3 changes.
#ifndef FASTLED_USE_PROGMEM
#define FASTLED_USE_PROGMEM 1
#endif
#endif

View File

@@ -0,0 +1,65 @@
#ifndef __INC_CLOCKLESS_ARM_KL26
#define __INC_CLOCKLESS_ARM_KL26
#include "../common/m0clockless.h"
FASTLED_NAMESPACE_BEGIN
#define FASTLED_HAS_CLOCKLESS 1
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPinBB<DATA_PIN>::port_t data_t;
data_t mPinMask;
data_ptr_t mPort;
CMinWait<WAIT_TIME> mWait;
public:
virtual void init() {
FastPinBB<DATA_PIN>::setOutput();
mPinMask = FastPinBB<DATA_PIN>::mask();
mPort = FastPinBB<DATA_PIN>::port();
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mWait.wait();
cli();
uint32_t clocks = showRGBInternal(pixels);
if(!clocks) {
sei(); delayMicroseconds(WAIT_TIME); cli();
clocks = showRGBInternal(pixels);
}
long microsTaken = CLKS_TO_MICROS(clocks * ((T1 + T2 + T3) * 24));
MS_COUNTER += (microsTaken / 1000);
sei();
mWait.mark();
}
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
// gcc will use register Y for the this pointer.
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
struct M0ClocklessData data;
data.d[0] = pixels.d[0];
data.d[1] = pixels.d[1];
data.d[2] = pixels.d[2];
data.s[0] = pixels.mScale[0];
data.s[1] = pixels.mScale[1];
data.s[2] = pixels.mScale[2];
data.e[0] = pixels.e[0];
data.e[1] = pixels.e[1];
data.e[2] = pixels.e[2];
data.adj = pixels.mAdvance;
typename FastPin<DATA_PIN>::port_ptr_t portBase = FastPin<DATA_PIN>::port();
return showLedData<4,8,T1,T2,T3,RGB_ORDER, WAIT_TIME>(portBase, FastPin<DATA_PIN>::mask(), pixels.mData, pixels.mLen, &data);
// return 0; // 0x00FFFFFF - _VAL;
}
};
FASTLED_NAMESPACE_END
#endif // __INC_CLOCKLESS_ARM_KL26

View File

@@ -0,0 +1,10 @@
#ifndef __INC_FASTLED_ARM_KL26_H
#define __INC_FASTLED_ARM_KL26_H
// Include the k20 headers
#include "fastpin_arm_kl26.h"
#include "fastspi_arm_kl26.h"
#include "clockless_arm_kl26.h"
#include "../k20/ws2812serial_controller.h"
#endif

View File

@@ -0,0 +1,88 @@
#ifndef __FASTPIN_ARM_KL26_H
#define __FASTPIN_ARM_KL26_H
FASTLED_NAMESPACE_BEGIN
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
#warning "Software pin support forced, pin access will be sloightly slower."
#define NO_HARDWARE_PIN_SUPPORT
#undef HAS_HARDWARE_PIN_SUPPORT
#else
/// Template definition for teensy LC style ARM pins, providing direct access to the various GPIO registers. Note that this
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
/// The registers are data output, set output, clear output, toggle output, input, and direction
template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN {
public:
typedef volatile uint32_t * port_ptr_t;
typedef uint32_t port_t;
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
inline static void hi() __attribute__ ((always_inline)) { _PSOR::r() = _MASK; }
inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; }
inline static void set(register port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
};
// Macros for kl26 pin access/definition
#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))
#define _R(T) struct __gen_struct_ ## T
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } \
template<int BIT> static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
#define _FL_IO(L,C) _RD32(FGPIO ## L ## _PDOR); _RD32(FGPIO ## L ## _PSOR); _RD32(FGPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(FGPIO ## L ## _PDIR); _RD32(FGPIO ## L ## _PDDR); _FL_DEFINE_PORT3(L,C,_R(FGPIO ## L ## _PDOR));
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << BIT, _R(FGPIO ## L ## _PDOR), _R(FGPIO ## L ## _PSOR), _R(FGPIO ## L ## _PCOR), \
_R(GPIO ## L ## _PTOR), _R(FGPIO ## L ## _PDIR), _R(FGPIO ## L ## _PDDR)> {}; \
/* template<> class FastPinBB<PIN> : public _ARMPIN_BITBAND<PIN, BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {}; */
_FL_IO(A,0); _FL_IO(B,1); _FL_IO(C,2); _FL_IO(D,3); _FL_IO(E,4);
// Actual pin definitions
#if defined(FASTLED_TEENSYLC) && defined(CORE_TEENSY)
#define MAX_PIN 26
_FL_DEFPIN(0, 16, B); _FL_DEFPIN(1, 17, B); _FL_DEFPIN(2, 0, D); _FL_DEFPIN(3, 1, A);
_FL_DEFPIN(4, 2, A); _FL_DEFPIN(5, 7, D); _FL_DEFPIN(6, 4, D); _FL_DEFPIN(7, 2, D);
_FL_DEFPIN(8, 3, D); _FL_DEFPIN(9, 3, C); _FL_DEFPIN(10, 4, C); _FL_DEFPIN(11, 6, C);
_FL_DEFPIN(12, 7, C); _FL_DEFPIN(13, 5, C); _FL_DEFPIN(14, 1, D); _FL_DEFPIN(15, 0, C);
_FL_DEFPIN(16, 0, B); _FL_DEFPIN(17, 1, B); _FL_DEFPIN(18, 3, B); _FL_DEFPIN(19, 2, B);
_FL_DEFPIN(20, 5, D); _FL_DEFPIN(21, 6, D); _FL_DEFPIN(22, 1, C); _FL_DEFPIN(23, 2, C);
_FL_DEFPIN(24, 20, E); _FL_DEFPIN(25, 21, E); _FL_DEFPIN(26, 30, E);
#define SPI_DATA 11
#define SPI_CLOCK 13
// #define SPI1 (*(SPI_t *)0x4002D000)
#define SPI2_DATA 0
#define SPI2_CLOCK 20
#define HAS_HARDWARE_PIN_SUPPORT
#endif
#endif // FASTLED_FORCE_SOFTWARE_PINS
FASTLED_NAMESPACE_END
#endif // __INC_FASTPIN_ARM_K20

View File

@@ -0,0 +1,252 @@
#ifndef __INC_FASTSPI_ARM_KL26_H
#define __INC_FASTSPI_ARM_KL26_h
FASTLED_NAMESPACE_BEGIN
template <int VAL> void getScalars(uint8_t & sppr, uint8_t & spr) {
if(VAL > 4096) { sppr=7; spr=8; }
else if(VAL > 3584) { sppr=6; spr=8; }
else if(VAL > 3072) { sppr=5; spr=8; }
else if(VAL > 2560) { sppr=4; spr=8; }
else if(VAL > 2048) { sppr=7; spr=7; }
else if(VAL > 2048) { sppr=3; spr=8; }
else if(VAL > 1792) { sppr=6; spr=7; }
else if(VAL > 1536) { sppr=5; spr=7; }
else if(VAL > 1536) { sppr=2; spr=8; }
else if(VAL > 1280) { sppr=4; spr=7; }
else if(VAL > 1024) { sppr=7; spr=6; }
else if(VAL > 1024) { sppr=3; spr=7; }
else if(VAL > 1024) { sppr=1; spr=8; }
else if(VAL > 896) { sppr=6; spr=6; }
else if(VAL > 768) { sppr=5; spr=6; }
else if(VAL > 768) { sppr=2; spr=7; }
else if(VAL > 640) { sppr=4; spr=6; }
else if(VAL > 512) { sppr=7; spr=5; }
else if(VAL > 512) { sppr=3; spr=6; }
else if(VAL > 512) { sppr=1; spr=7; }
else if(VAL > 512) { sppr=0; spr=8; }
else if(VAL > 448) { sppr=6; spr=5; }
else if(VAL > 384) { sppr=5; spr=5; }
else if(VAL > 384) { sppr=2; spr=6; }
else if(VAL > 320) { sppr=4; spr=5; }
else if(VAL > 256) { sppr=7; spr=4; }
else if(VAL > 256) { sppr=3; spr=5; }
else if(VAL > 256) { sppr=1; spr=6; }
else if(VAL > 256) { sppr=0; spr=7; }
else if(VAL > 224) { sppr=6; spr=4; }
else if(VAL > 192) { sppr=5; spr=4; }
else if(VAL > 192) { sppr=2; spr=5; }
else if(VAL > 160) { sppr=4; spr=4; }
else if(VAL > 128) { sppr=7; spr=3; }
else if(VAL > 128) { sppr=3; spr=4; }
else if(VAL > 128) { sppr=1; spr=5; }
else if(VAL > 128) { sppr=0; spr=6; }
else if(VAL > 112) { sppr=6; spr=3; }
else if(VAL > 96) { sppr=5; spr=3; }
else if(VAL > 96) { sppr=2; spr=4; }
else if(VAL > 80) { sppr=4; spr=3; }
else if(VAL > 64) { sppr=7; spr=2; }
else if(VAL > 64) { sppr=3; spr=3; }
else if(VAL > 64) { sppr=1; spr=4; }
else if(VAL > 64) { sppr=0; spr=5; }
else if(VAL > 56) { sppr=6; spr=2; }
else if(VAL > 48) { sppr=5; spr=2; }
else if(VAL > 48) { sppr=2; spr=3; }
else if(VAL > 40) { sppr=4; spr=2; }
else if(VAL > 32) { sppr=7; spr=1; }
else if(VAL > 32) { sppr=3; spr=2; }
else if(VAL > 32) { sppr=1; spr=3; }
else if(VAL > 32) { sppr=0; spr=4; }
else if(VAL > 28) { sppr=6; spr=1; }
else if(VAL > 24) { sppr=5; spr=1; }
else if(VAL > 24) { sppr=2; spr=2; }
else if(VAL > 20) { sppr=4; spr=1; }
else if(VAL > 16) { sppr=7; spr=0; }
else if(VAL > 16) { sppr=3; spr=1; }
else if(VAL > 16) { sppr=1; spr=2; }
else if(VAL > 16) { sppr=0; spr=3; }
else if(VAL > 14) { sppr=6; spr=0; }
else if(VAL > 12) { sppr=5; spr=0; }
else if(VAL > 12) { sppr=2; spr=1; }
else if(VAL > 10) { sppr=4; spr=0; }
else if(VAL > 8) { sppr=3; spr=0; }
else if(VAL > 8) { sppr=1; spr=1; }
else if(VAL > 8) { sppr=0; spr=2; }
else if(VAL > 6) { sppr=2; spr=0; }
else if(VAL > 4) { sppr=1; spr=0; }
else if(VAL > 4) { sppr=0; spr=1; }
else /* if(VAL > 2) */ { sppr=0; spr=0; }
}
#define SPIX (*(KINETISL_SPI_t*)pSPIX)
#define ARM_HARDWARE_SPI
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX>
class ARMHardwareSPIOutput {
Selectable *m_pSelect;
static inline void enable_pins(void) __attribute__((always_inline)) {
switch(_DATA_PIN) {
case 0: CORE_PIN0_CONFIG = PORT_PCR_MUX(2); break;
case 1: CORE_PIN1_CONFIG = PORT_PCR_MUX(5); break;
case 7: CORE_PIN7_CONFIG = PORT_PCR_MUX(2); break;
case 8: CORE_PIN8_CONFIG = PORT_PCR_MUX(5); break;
case 11: CORE_PIN11_CONFIG = PORT_PCR_MUX(2); break;
case 12: CORE_PIN12_CONFIG = PORT_PCR_MUX(5); break;
case 21: CORE_PIN21_CONFIG = PORT_PCR_MUX(2); break;
}
switch(_CLOCK_PIN) {
case 13: CORE_PIN13_CONFIG = PORT_PCR_MUX(2); break;
case 14: CORE_PIN14_CONFIG = PORT_PCR_MUX(2); break;
case 20: CORE_PIN20_CONFIG = PORT_PCR_MUX(2); break;
}
}
static inline void disable_pins(void) __attribute((always_inline)) {
switch(_DATA_PIN) {
case 0: CORE_PIN0_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
case 1: CORE_PIN1_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
case 8: CORE_PIN8_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
case 12: CORE_PIN12_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
case 21: CORE_PIN21_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
}
switch(_CLOCK_PIN) {
case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
case 20: CORE_PIN20_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
}
}
void setSPIRate() {
uint8_t sppr, spr;
getScalars<_SPI_CLOCK_DIVIDER>(sppr, spr);
// Set the speed
SPIX.BR = SPI_BR_SPPR(sppr) | SPI_BR_SPR(spr);
// Also, force 8 bit transfers (don't want to juggle 8/16 since that flushes the world)
SPIX.C2 = 0;
SPIX.C1 |= SPI_C1_SPE;
}
public:
ARMHardwareSPIOutput() { m_pSelect = NULL; }
ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
// set the object representing the selectable
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
// initialize the SPI subssytem
void init() {
FastPin<_DATA_PIN>::setOutput();
FastPin<_CLOCK_PIN>::setOutput();
// Enable the SPI clocks
uint32_t sim4 = SIM_SCGC4;
if ((pSPIX == 0x40076000) && !(sim4 & SIM_SCGC4_SPI0)) {
SIM_SCGC4 = sim4 | SIM_SCGC4_SPI0;
}
if ( (pSPIX == 0x40077000) && !(sim4 & SIM_SCGC4_SPI1)) {
SIM_SCGC4 = sim4 | SIM_SCGC4_SPI1;
}
SPIX.C1 = SPI_C1_MSTR | SPI_C1_SPE;
SPIX.C2 = 0;
SPIX.BR = SPI_BR_SPPR(1) | SPI_BR_SPR(0);
}
// latch the CS select
void inline select() __attribute__((always_inline)) {
if(m_pSelect != NULL) { m_pSelect->select(); }
setSPIRate();
enable_pins();
}
// release the CS select
void inline release() __attribute__((always_inline)) {
disable_pins();
if(m_pSelect != NULL) { m_pSelect->release(); }
}
// Wait for the world to be clear
static void wait() __attribute__((always_inline)) { while(!(SPIX.S & SPI_S_SPTEF)); }
// wait until all queued up data has been written
void waitFully() { wait(); }
// not the most efficient mechanism in the world - but should be enough for sm16716 and friends
template <uint8_t BIT> inline static void writeBit(uint8_t b) { /* TODO */ }
// write a byte out via SPI (returns immediately on writing register)
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.DL = b; }
// write a word out via SPI (returns immediately on writing register)
static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w & 0xFF); }
// A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
static void writeBytesValueRaw(uint8_t value, int len) {
while(len--) { writeByte(value); }
}
// A full cycle of writing a value for len bytes, including select, release, and waiting
void writeBytesValue(uint8_t value, int len) {
setSPIRate();
select();
while(len--) {
writeByte(value);
}
waitFully();
release();
}
// A full cycle of writing a raw block of data out, including select, release, and waiting
template <class D> void writeBytes(register uint8_t *data, int len) {
setSPIRate();
uint8_t *end = data + len;
select();
// could be optimized to write 16bit words out instead of 8bit bytes
while(data != end) {
writeByte(D::adjust(*data++));
}
D::postBlock(len);
waitFully();
release();
}
void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
int len = pixels.mLen;
select();
while(pixels.has(1)) {
if(FLAGS & FLAG_START_BIT) {
writeBit<0>(1);
writeByte(D::adjust(pixels.loadAndScale0()));
writeByte(D::adjust(pixels.loadAndScale1()));
writeByte(D::adjust(pixels.loadAndScale2()));
} else {
writeByte(D::adjust(pixels.loadAndScale0()));
writeByte(D::adjust(pixels.loadAndScale1()));
writeByte(D::adjust(pixels.loadAndScale2()));
}
pixels.advanceData();
pixels.stepDithering();
}
D::postBlock(len);
release();
}
};
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,47 @@
#ifndef __INC_LED_SYSDEFS_ARM_KL26_H
#define __INC_LED_SYSDEFS_ARM_KL26_H
#define FASTLED_TEENSYLC
#define FASTLED_ARM
#define FASTLED_ARM_M0_PLUS
#ifndef INTERRUPT_THRESHOLD
#define INTERRUPT_THRESHOLD 1
#endif
#define FASTLED_SPI_BYTE_ONLY
// Default to allowing interrupts
#ifndef FASTLED_ALLOW_INTERRUPTS
// #define FASTLED_ALLOW_INTERRUPTS 1
#endif
#if FASTLED_ALLOW_INTERRUPTS == 1
#define FASTLED_ACCURATE_CLOCK
#endif
#if (F_CPU == 96000000)
#define CLK_DBL 1
#endif
// Get some system include files
#include <avr/io.h>
#include <avr/interrupt.h> // for cli/se definitions
// Define the register types
#if defined(ARDUINO) // && ARDUINO < 150
typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
#endif
extern volatile uint32_t systick_millis_count;
# define MS_COUNTER systick_millis_count
// Default to using PROGMEM since TEENSYLC provides it
// even though all it does is ignore it. Just being
// conservative here in case TEENSYLC changes.
#ifndef FASTLED_USE_PROGMEM
#define FASTLED_USE_PROGMEM 1
#endif
#endif

View File

@@ -0,0 +1,212 @@
#ifndef __INC_BLOCK_CLOCKLESS_ARM_MXRT1062_H
#define __INC_BLOCK_CLOCKLESS_ARM_MXRT1062_H
FASTLED_NAMESPACE_BEGIN
// Definition for a single channel clockless controller for the teensy4
// See clockless.h for detailed info on how the template parameters are used.
#if defined(FASTLED_TEENSY4)
#define __FL_T4_MASK ((1<<(LANES))-1)
template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class FlexibleInlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, __FL_T4_MASK> {
uint8_t m_bitOffsets[16];
uint8_t m_nActualLanes;
uint8_t m_nLowBit;
uint8_t m_nHighBit;
uint32_t m_nWriteMask;
uint8_t m_nOutBlocks;
uint32_t m_offsets[3];
CMinWait<WAIT_TIME> mWait;
public:
virtual int size() { return CLEDController::size() * m_nActualLanes; }
// For each pin, if we've hit our lane count, break, otherwise set the pin to output,
// store the bit offset in our offset array, add this pin to the write mask, and if this
// pin ends a block sequence, then break out of the switch as well
#define _BLOCK_PIN(P) case P: { \
if(m_nActualLanes == LANES) break; \
FastPin<P>::setOutput(); \
m_bitOffsets[m_nActualLanes++] = FastPin<P>::pinbit(); \
m_nWriteMask |= FastPin<P>::mask(); \
if( P == 27 || P == 7 || P == 30) break; \
}
virtual void init() {
// pre-initialize
memset(m_bitOffsets,0,16);
m_nActualLanes = 0;
m_nLowBit = 33;
m_nHighBit = 0;
m_nWriteMask = 0;
// setup the bits and data tracking for parallel output
switch(FIRST_PIN) {
// GPIO6 block output
_BLOCK_PIN( 1);
_BLOCK_PIN( 0);
_BLOCK_PIN(24);
_BLOCK_PIN(25);
_BLOCK_PIN(19);
_BLOCK_PIN(18);
_BLOCK_PIN(14);
_BLOCK_PIN(15);
_BLOCK_PIN(17);
_BLOCK_PIN(16);
_BLOCK_PIN(22);
_BLOCK_PIN(23);
_BLOCK_PIN(20);
_BLOCK_PIN(21);
_BLOCK_PIN(26);
_BLOCK_PIN(27);
// GPIO7 block output
_BLOCK_PIN(10);
_BLOCK_PIN(12);
_BLOCK_PIN(11);
_BLOCK_PIN(13);
_BLOCK_PIN( 6);
_BLOCK_PIN( 9);
_BLOCK_PIN(32);
_BLOCK_PIN( 8);
_BLOCK_PIN( 7);
// GPIO 37 block output
_BLOCK_PIN(37);
_BLOCK_PIN(36);
_BLOCK_PIN(35);
_BLOCK_PIN(34);
_BLOCK_PIN(39);
_BLOCK_PIN(38);
_BLOCK_PIN(28);
_BLOCK_PIN(31);
_BLOCK_PIN(30);
}
for(int i = 0; i < m_nActualLanes; ++i) {
if(m_bitOffsets[i] < m_nLowBit) { m_nLowBit = m_bitOffsets[i]; }
if(m_bitOffsets[i] > m_nHighBit) { m_nHighBit = m_bitOffsets[i]; }
}
m_nOutBlocks = (m_nHighBit + 8)/8;
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
virtual void showPixels(PixelController<RGB_ORDER, LANES, __FL_T4_MASK> & pixels) {
mWait.wait();
#if FASTLED_ALLOW_INTERRUPTS == 0
uint32_t clocks = showRGBInternal(pixels);
// Adjust the timer
long microsTaken = CLKS_TO_MICROS(clocks);
MS_COUNTER += (1 + (microsTaken / 1000));
#else
showRGBInternal(pixels);
#endif
mWait.mark();
}
typedef union {
uint8_t bytes[32];
uint8_t bg[4][8];
uint16_t shorts[16];
uint32_t raw[8];
} _outlines;
template<int BITS,int PX> __attribute__ ((always_inline)) inline void writeBits(register uint32_t & next_mark, register _outlines & b, PixelController<RGB_ORDER, LANES, __FL_T4_MASK> &pixels) {
_outlines b2;
transpose8x1(b.bg[3], b2.bg[3]);
transpose8x1(b.bg[2], b2.bg[2]);
transpose8x1(b.bg[1], b2.bg[1]);
transpose8x1(b.bg[0], b2.bg[0]);
register uint8_t d = pixels.template getd<PX>(pixels);
register uint8_t scale = pixels.template getscale<PX>(pixels);
int x = 0;
for(uint32_t i = 8; i > 0;) {
--i;
while(ARM_DWT_CYCCNT < next_mark);
*FastPin<FIRST_PIN>::sport() = m_nWriteMask;
next_mark = ARM_DWT_CYCCNT + m_offsets[0];
uint32_t out = (b2.bg[3][i] << 24) | (b2.bg[2][i] << 16) | (b2.bg[1][i] << 8) | b2.bg[0][i];
out = ((~out) & m_nWriteMask);
while((next_mark - ARM_DWT_CYCCNT) > m_offsets[1]);
*FastPin<FIRST_PIN>::cport() = out;
out = m_nWriteMask;
while((next_mark - ARM_DWT_CYCCNT) > m_offsets[2]);
*FastPin<FIRST_PIN>::cport() = out;
// Read and store up to two bytes
if (x < m_nActualLanes) {
b.bytes[m_bitOffsets[x]] = pixels.template loadAndScale<PX>(pixels, x, d, scale);
++x;
if (x < m_nActualLanes) {
b.bytes[m_bitOffsets[x]] = pixels.template loadAndScale<PX>(pixels, x, d, scale);
++x;
}
}
}
}
uint32_t showRGBInternal(PixelController<RGB_ORDER,LANES, __FL_T4_MASK> &allpixels) {
allpixels.preStepFirstByteDithering();
_outlines b0;
uint32_t start = ARM_DWT_CYCCNT;
for(int i = 0; i < m_nActualLanes; ++i) {
b0.bytes[m_bitOffsets[i]] = allpixels.loadAndScale0(i);
}
cli();
m_offsets[0] = _FASTLED_NS_TO_DWT(T1+T2+T3);
m_offsets[1] = _FASTLED_NS_TO_DWT(T2+T3);
m_offsets[2] = _FASTLED_NS_TO_DWT(T3);
uint32_t wait_off = _FASTLED_NS_TO_DWT((WAIT_TIME-INTERRUPT_THRESHOLD));
uint32_t next_mark = ARM_DWT_CYCCNT + m_offsets[0];
while(allpixels.has(1)) {
allpixels.stepDithering();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
// if interrupts took longer than 45µs, punt on the current frame
if(ARM_DWT_CYCCNT > next_mark) {
if((ARM_DWT_CYCCNT-next_mark) > wait_off) { sei(); return ARM_DWT_CYCCNT - start; }
}
#endif
// Write first byte, read next byte
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
// Write second byte, read 3rd byte
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
allpixels.advanceData();
// Write third byte
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
#if (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
}
sei();
return ARM_DWT_CYCCNT - start;
}
};
template<template<uint8_t DATA_PIN, EOrder RGB_ORDER> class CHIPSET, uint8_t DATA_PIN, int NUM_LANES, EOrder RGB_ORDER=GRB>
class __FIBCC : public FlexibleInlineBlockClocklessController<NUM_LANES,DATA_PIN,CHIPSET<DATA_PIN,RGB_ORDER>::__T1(),CHIPSET<DATA_PIN,RGB_ORDER>::__T2(),CHIPSET<DATA_PIN,RGB_ORDER>::__T3(),RGB_ORDER,CHIPSET<DATA_PIN,RGB_ORDER>::__XTRA0(),CHIPSET<DATA_PIN,RGB_ORDER>::__FLIP(),CHIPSET<DATA_PIN,RGB_ORDER>::__WAIT_TIME()> {};
#define __FASTLED_HAS_FIBCC 1
#endif //defined(FASTLED_TEENSY4)
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,131 @@
#ifndef __INC_CLOCKLESS_ARM_MXRT1062_H
#define __INC_CLOCKLESS_ARM_MXRT1062_H
FASTLED_NAMESPACE_BEGIN
// Definition for a single channel clockless controller for the teensy4
// See clockless.h for detailed info on how the template parameters are used.
#if defined(FASTLED_TEENSY4)
#define FASTLED_HAS_CLOCKLESS 1
#define _FASTLED_NS_TO_DWT(_NS) (((F_CPU_ACTUAL>>16)*(_NS)) / (1000000000UL>>16))
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
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;
uint32_t off[3];
public:
static constexpr int __DATA_PIN() { return DATA_PIN; }
static constexpr int __T1() { return T1; }
static constexpr int __T2() { return T2; }
static constexpr int __T3() { return T3; }
static constexpr EOrder __RGB_ORDER() { return RGB_ORDER; }
static constexpr int __XTRA0() { return XTRA0; }
static constexpr bool __FLIP() { return FLIP; }
static constexpr int __WAIT_TIME() { return WAIT_TIME; }
virtual void init() {
FastPin<DATA_PIN>::setOutput();
mPinMask = FastPin<DATA_PIN>::mask();
mPort = FastPin<DATA_PIN>::port();
FastPin<DATA_PIN>::lo();
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mWait.wait();
if(!showRGBInternal(pixels)) {
sei(); delayMicroseconds(WAIT_TIME); cli();
showRGBInternal(pixels);
}
mWait.mark();
}
template<int BITS> __attribute__ ((always_inline)) inline void writeBits(register uint32_t & next_mark, register uint32_t & b) {
for(register uint32_t i = BITS-1; i > 0; --i) {
while(ARM_DWT_CYCCNT < next_mark);
next_mark = ARM_DWT_CYCCNT + off[0];
FastPin<DATA_PIN>::hi();
if(b&0x80) {
while((next_mark - ARM_DWT_CYCCNT) > off[1]);
FastPin<DATA_PIN>::lo();
} else {
while((next_mark - ARM_DWT_CYCCNT) > off[2]);
FastPin<DATA_PIN>::lo();
}
b <<= 1;
}
while(ARM_DWT_CYCCNT < next_mark);
next_mark = ARM_DWT_CYCCNT + off[1];
FastPin<DATA_PIN>::hi();
if(b&0x80) {
while((next_mark - ARM_DWT_CYCCNT) > off[2]);
FastPin<DATA_PIN>::lo();
} else {
while((next_mark - ARM_DWT_CYCCNT) > off[2]);
FastPin<DATA_PIN>::lo();
}
}
uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
uint32_t start = ARM_DWT_CYCCNT;
// Setup the pixel controller and load/scale the first byte
pixels.preStepFirstByteDithering();
register uint32_t b = pixels.loadAndScale0();
cli();
off[0] = _FASTLED_NS_TO_DWT(T1+T2+T3);
off[1] = _FASTLED_NS_TO_DWT(T2+T3);
off[2] = _FASTLED_NS_TO_DWT(T3);
uint32_t wait_off = _FASTLED_NS_TO_DWT((WAIT_TIME-INTERRUPT_THRESHOLD));
uint32_t next_mark = ARM_DWT_CYCCNT + off[0];
while(pixels.has(1)) {
pixels.stepDithering();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
// if interrupts took longer than 45µs, punt on the current frame
if(ARM_DWT_CYCCNT > next_mark) {
if((ARM_DWT_CYCCNT-next_mark) > wait_off) { sei(); return ARM_DWT_CYCCNT - start; }
}
#endif
// Write first byte, read next byte
writeBits<8+XTRA0>(next_mark, b);
b = pixels.loadAndScale1();
// Write second byte, read 3rd byte
writeBits<8+XTRA0>(next_mark, b);
b = pixels.loadAndScale2();
// Write third byte, read 1st byte of next pixel
writeBits<8+XTRA0>(next_mark, b);
b = pixels.advanceAndLoadAndScale0();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
};
sei();
return ARM_DWT_CYCCNT - start;
}
};
#endif
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,12 @@
#ifndef __INC_FASTLED_ARM_MXRT1062_H
#define __INC_FASTLED_ARM_MXRT1062_H
#include "fastpin_arm_mxrt1062.h"
#include "fastspi_arm_mxrt1062.h"
#include "../k20/octows2811_controller.h"
#include "../k20/ws2812serial_controller.h"
#include "../k20/smartmatrix_t3.h"
#include "clockless_arm_mxrt1062.h"
#include "block_clockless_arm_mxrt1062.h"
#endif

View File

@@ -0,0 +1,91 @@
#ifndef __FASTPIN_ARM_MXRT1062_H
#define __FASTPIN_ARM_MXRT1062_H
FASTLED_NAMESPACE_BEGIN
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
#warning "Software pin support forced, pin access will be slightly slower."
#define NO_HARDWARE_PIN_SUPPORT
#undef HAS_HARDWARE_PIN_SUPPORT
#else
/// Template definition for teensy 4.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
/// uses the full port GPIO registers. It calls through to pinMode for setting input/output on pins
/// The registers are data output, set output, clear output, toggle output, input, and direction
template<uint8_t PIN, uint32_t _BIT, uint32_t _MASK, typename _GPIO_DR, typename _GPIO_DR_SET, typename _GPIO_DR_CLEAR, typename _GPIO_DR_TOGGLE> 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)) { _GPIO_DR_SET::r() = _MASK; }
inline static void lo() __attribute__ ((always_inline)) { _GPIO_DR_CLEAR::r() = _MASK; }
inline static void set(register port_t val) __attribute__ ((always_inline)) { _GPIO_DR::r() = val; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline static void toggle() __attribute__ ((always_inline)) { _GPIO_DR_TOGGLE::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 _GPIO_DR::r() | _MASK; }
inline static port_t loval() __attribute__ ((always_inline)) { return _GPIO_DR::r() & ~_MASK; }
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_GPIO_DR::r(); }
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPIO_DR_SET::r(); }
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPIO_DR_CLEAR::r(); }
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
inline static uint32_t pinbit() __attribute__ ((always_inline)) { return _BIT; }
};
#define _R(T) struct __gen_struct_ ## T
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } };
#define _FL_IO(L) _RD32(GPIO ## L ## _DR); _RD32(GPIO ## L ## _DR_SET); _RD32(GPIO ## L ## _DR_CLEAR); _RD32(GPIO ## L ## _DR_TOGGLE); _FL_DEFINE_PORT(L, _R(GPIO ## L ## _DR));
// From the teensy core - it looks like there's the "default set" of port registers at GPIO1-5 - but then there
// are a mirrored set for GPIO1-4 at GPIO6-9, which in the teensy core is referred to as "fast" - while the pin definitiosn
// at https://forum.pjrc.com/threads/54711-Teensy-4-0-First-Beta-Test?p=193716&viewfull=1#post193716
// refer to GPIO1-4, we're going to use GPIO6-9 in the definitions below because the fast registers are what
// the teensy core is using internally
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1 << BIT, _R(GPIO ## L ## _DR), _R(GPIO ## L ## _DR_SET), _R(GPIO ## L ## _DR_CLEAR), _R(GPIO ## L ## _DR_TOGGLE)> {};
#if defined(FASTLED_TEENSY4) && defined(CORE_TEENSY)
_FL_IO(1); _FL_IO(2); _FL_IO(3); _FL_IO(4); _FL_IO(5);
_FL_IO(6); _FL_IO(7); _FL_IO(8); _FL_IO(9);
#define MAX_PIN 39
_FL_DEFPIN( 0, 3,6); _FL_DEFPIN( 1, 2,6); _FL_DEFPIN( 2, 4,9); _FL_DEFPIN( 3, 5,9);
_FL_DEFPIN( 4, 6,9); _FL_DEFPIN( 5, 8,9); _FL_DEFPIN( 6,10,7); _FL_DEFPIN( 7,17,7);
_FL_DEFPIN( 8,16,7); _FL_DEFPIN( 9,11,7); _FL_DEFPIN(10, 0,7); _FL_DEFPIN(11, 2,7);
_FL_DEFPIN(12, 1,7); _FL_DEFPIN(13, 3,7); _FL_DEFPIN(14,18,6); _FL_DEFPIN(15,19,6);
_FL_DEFPIN(16,23,6); _FL_DEFPIN(17,22,6); _FL_DEFPIN(18,17,6); _FL_DEFPIN(19,16,6);
_FL_DEFPIN(20,26,6); _FL_DEFPIN(21,27,6); _FL_DEFPIN(22,24,6); _FL_DEFPIN(23,25,6);
_FL_DEFPIN(24,12,6); _FL_DEFPIN(25,13,6); _FL_DEFPIN(26,30,6); _FL_DEFPIN(27,31,6);
_FL_DEFPIN(28,18,8); _FL_DEFPIN(29,31,9); _FL_DEFPIN(30,23,8); _FL_DEFPIN(31,22,8);
_FL_DEFPIN(32,12,7); _FL_DEFPIN(33, 7,9); _FL_DEFPIN(34,15,8); _FL_DEFPIN(35,14,8);
_FL_DEFPIN(36,13,8); _FL_DEFPIN(37,12,8); _FL_DEFPIN(38,17,8); _FL_DEFPIN(39,16,8);
#define HAS_HARDWARE_PIN_SUPPORT
#define ARM_HARDWARE_SPI
#define SPI_DATA 11
#define SPI_CLOCK 13
#define SPI1_DATA 26
#define SPI1_CLOCK 27
#define SPI2_DATA 35
#define SPI2_CLOCK 37
#endif // defined FASTLED_TEENSY4
#endif // FASTLED_FORCE_SOFTWARE_PINSs
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,140 @@
#ifndef __INC_FASTSPI_ARM_MXRT1062_H
#define __INC_FASTSPI_ARM_MXRT1062_H
FASTLED_NAMESPACE_BEGIN
#if defined (FASTLED_TEENSY4) && defined(ARM_HARDWARE_SPI)
#include <SPI.h>
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_RATE, SPIClass & _SPIObject, int _SPI_INDEX>
class Teesy4HardwareSPIOutput {
Selectable *m_pSelect;
uint32_t m_bitCount;
uint32_t m_bitData;
inline IMXRT_LPSPI_t & port() __attribute__((always_inline)) {
switch(_SPI_INDEX) {
case 0:
return IMXRT_LPSPI4_S;
case 1:
return IMXRT_LPSPI3_S;
case 2:
return IMXRT_LPSPI1_S;
}
}
public:
Teesy4HardwareSPIOutput() { m_pSelect = NULL; m_bitCount = 0;}
Teesy4HardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; m_bitCount = 0;}
// set the object representing the selectable -- ignore for now
void setSelect(Selectable *pSelect) { /* TODO */ }
// initialize the SPI subssytem
void init() { _SPIObject.begin(); }
// latch the CS select
void inline select() __attribute__((always_inline)) {
// begin the SPI transaction
_SPIObject.beginTransaction(SPISettings(_SPI_CLOCK_RATE, MSBFIRST, SPI_MODE0));
if(m_pSelect != NULL) { m_pSelect->select(); }
}
// release the CS select
void inline release() __attribute__((always_inline)) {
if(m_pSelect != NULL) { m_pSelect->release(); }
_SPIObject.endTransaction();
}
// wait until all queued up data has been written
static void waitFully() { /* TODO */ }
// write a byte out via SPI (returns immediately on writing register) -
void inline writeByte(uint8_t b) __attribute__((always_inline)) {
if(m_bitCount == 0) {
_SPIObject.transfer(b);
} else {
// There's been a bit of data written, add that to the output as well
uint32_t outData = (m_bitData << 8) | b;
uint32_t tcr = port().TCR;
port().TCR = (tcr & 0xfffff000) | LPSPI_TCR_FRAMESZ((8+m_bitCount) - 1); // turn on 9 bit mode
port().TDR = outData; // output 9 bit data.
while ((port().RSR & LPSPI_RSR_RXEMPTY)) ; // wait while the RSR fifo is empty...
port().TCR = (tcr & 0xfffff000) | LPSPI_TCR_FRAMESZ((8) - 1); // turn back on 8 bit mode
port().RDR;
m_bitCount = 0;
}
}
// write a word out via SPI (returns immediately on writing register)
void inline writeWord(uint16_t w) __attribute__((always_inline)) {
writeByte(((w>>8) & 0xFF));
_SPIObject.transfer(w & 0xFF);
}
// A raw set of writing byte values, assumes setup/init/waiting done elsewhere
static void writeBytesValueRaw(uint8_t value, int len) {
while(len--) { _SPIObject.transfer(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 void writeBit(uint8_t b) {
m_bitData = (m_bitData<<1) | ((b&(1<<BIT)) != 0);
// If this is the 8th bit we've collected, just write it out raw
register uint32_t bc = m_bitCount;
bc = (bc + 1) & 0x07;
if (!bc) {
m_bitCount = 0;
_SPIObject.transfer(m_bitData);
}
m_bitCount = bc;
}
// 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;
while(pixels.has(1)) {
if(FLAGS & FLAG_START_BIT) {
writeBit<0>(1);
}
writeByte(D::adjust(pixels.loadAndScale0()));
writeByte(D::adjust(pixels.loadAndScale1()));
writeByte(D::adjust(pixels.loadAndScale2()));
pixels.advanceData();
pixels.stepDithering();
}
D::postBlock(len);
release();
}
};
#endif
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,43 @@
#ifndef __INC_LED_SYSDEFS_ARM_MXRT1062_H
#define __INC_LED_SYSDEFS_ARM_MXRT1062_H
#define FASTLED_TEENSY4
#define FASTLED_ARM
#ifndef INTERRUPT_THRESHOLD
#define INTERRUPT_THRESHOLD 1
#endif
// Default to allowing interrupts
#ifndef FASTLED_ALLOW_INTERRUPTS
#define FASTLED_ALLOW_INTERRUPTS 1
#endif
#if FASTLED_ALLOW_INTERRUPTS == 1
#define FASTLED_ACCURATE_CLOCK
#endif
#if (F_CPU == 96000000)
#define CLK_DBL 1
#endif
// Get some system include files
#include <avr/io.h>
#include <avr/interrupt.h> // for cli/se definitions
// Define the register types
#if defined(ARDUINO) // && ARDUINO < 150
typedef volatile uint32_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
typedef volatile uint32_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
#endif
// extern volatile uint32_t systick_millis_count;
// # define MS_COUNTER systick_millis_count
// Teensy4 provides progmem
#ifndef FASTLED_USE_PROGMEM
#define FASTLED_USE_PROGMEM 1
#endif
#endif

View File

@@ -0,0 +1,84 @@
#ifndef __INC_CLOCKLESS_ARM_NRF51
#define __INC_CLOCKLESS_ARM_NRF51
#if defined(NRF51)
#include <nrf51_bitfields.h>
#define FASTLED_HAS_CLOCKLESS 1
#if (FASTLED_ALLOW_INTERRUPTS==1)
#define SEI_CHK LED_TIMER->CC[0] = (WAIT_TIME * (F_CPU/1000000)); LED_TIMER->TASKS_CLEAR; LED_TIMER->EVENTS_COMPARE[0] = 0;
#define CLI_CHK cli(); if(LED_TIMER->EVENTS_COMPARE[0]) { LED_TIMER->TASKS_STOP = 1; return 0; }
#define INNER_SEI sei();
#else
#define SEI_CHK
#define CLI_CHK
#define INNER_SEI delaycycles<1>();
#endif
#include "../common/m0clockless.h"
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 75>
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPinBB<DATA_PIN>::port_t data_t;
data_t mPinMask;
data_ptr_t mPort;
CMinWait<WAIT_TIME> mWait;
public:
virtual void init() {
FastPinBB<DATA_PIN>::setOutput();
mPinMask = FastPinBB<DATA_PIN>::mask();
mPort = FastPinBB<DATA_PIN>::port();
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mWait.wait();
cli();
if(!showRGBInternal(pixels)) {
sei(); delayMicroseconds(WAIT_TIME); cli();
showRGBInternal(pixels);
}
sei();
mWait.mark();
}
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
// gcc will use register Y for the this pointer.
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
struct M0ClocklessData data;
data.d[0] = pixels.d[0];
data.d[1] = pixels.d[1];
data.d[2] = pixels.d[2];
data.s[0] = pixels.mScale[0];
data.s[1] = pixels.mScale[1];
data.s[2] = pixels.mScale[2];
data.e[0] = pixels.e[0];
data.e[1] = pixels.e[1];
data.e[2] = pixels.e[2];
data.adj = pixels.mAdvance;
typename FastPin<DATA_PIN>::port_ptr_t portBase = FastPin<DATA_PIN>::port();
// timer mode w/prescaler of 0
LED_TIMER->MODE = TIMER_MODE_MODE_Timer;
LED_TIMER->PRESCALER = 0;
LED_TIMER->EVENTS_COMPARE[0] = 0;
LED_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
LED_TIMER->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk;
LED_TIMER->TASKS_START = 1;
int ret = showLedData<4,8,T1,T2,T3,RGB_ORDER,WAIT_TIME>(portBase, FastPin<DATA_PIN>::mask(), pixels.mData, pixels.mLen, &data);
LED_TIMER->TASKS_STOP = 1;
return ret; // 0x00FFFFFF - _VAL;
}
};
#endif // NRF51
#endif // __INC_CLOCKLESS_ARM_NRF51

View File

@@ -0,0 +1,9 @@
#ifndef __INC_FASTLED_ARM_NRF51_H
#define __INC_FASTLED_ARM_NRF51_H
// Include the k20 headers
#include "fastpin_arm_nrf51.h"
#include "fastspi_arm_nrf51.h"
#include "clockless_arm_nrf51.h"
#endif

View File

@@ -0,0 +1,119 @@
#ifndef __FASTPIN_ARM_NRF51_H
#define __FASTPIN_ARM_NRF51_H
#if defined(NRF51)
/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
/// The registers are data output, set output, clear output, toggle output, input, and direction
#if 0
template<uint8_t PIN, uint32_t _MASK, typename _DIRSET, typename _DIRCLR, typename _OUTSET, typename _OUTCLR, typename _OUT> class _ARMPIN {
public:
typedef volatile uint32_t * port_ptr_t;
typedef uint32_t port_t;
inline static void setOutput() { _DIRSET::r() = _MASK; }
inline static void setInput() { _DIRCLR::r() = _MASK; }
inline static void hi() __attribute__ ((always_inline)) { _OUTSET::r() = _MASK; }
inline static void lo() __attribute__ ((always_inline)) { _OUTCLR::r() = _MASK; }
inline static void set(register port_t val) __attribute__ ((always_inline)) { _OUT::r() = val; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline static void toggle() __attribute__ ((always_inline)) { _OUT::r() ^= _MASK; }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
inline static port_t hival() __attribute__ ((always_inline)) { return _OUT::r() | _MASK; }
inline static port_t loval() __attribute__ ((always_inline)) { return _OUT::r() & ~_MASK; }
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_OUT::r(); }
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
};
#define ADDR(X) *(volatile uint32_t*)X
#define NR_GPIO_ADDR(base,offset) (*(volatile uint32_t *))((uint32_t)(base + offset))
#define NR_DIRSET ADDR(0x50000518UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x518)
#define NR_DIRCLR ADDR(0x5000051CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x51C)
#define NR_OUTSET ADDR(0x50000508UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x508)
#define NR_OUTCLR ADDR(0x5000050CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x50C)
#define NR_OUT ADDR(0x50000504UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x504)
#define _RD32_NRF(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; }};
_RD32_NRF(NR_DIRSET);
_RD32_NRF(NR_DIRCLR);
_RD32_NRF(NR_OUTSET);
_RD32_NRF(NR_OUTCLR);
_RD32_NRF(NR_OUT);
#define _FL_DEFPIN(PIN) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << PIN, \
_R(NR_DIRSET), _R(NR_DIRCLR), _R(NR_OUTSET), _R(NR_OUTCLR), _R(NR_OUT)> {};
#else
typedef struct { /*!< GPIO Structure */
// __I uint32_t RESERVED0[321];
__IO uint32_t OUT; /*!< Write GPIO port. */
__IO uint32_t OUTSET; /*!< Set individual bits in GPIO port. */
__IO uint32_t OUTCLR; /*!< Clear individual bits in GPIO port. */
__I uint32_t IN; /*!< Read GPIO port. */
__IO uint32_t DIR; /*!< Direction of GPIO pins. */
__IO uint32_t DIRSET; /*!< DIR set register. */
__IO uint32_t DIRCLR; /*!< DIR clear register. */
__I uint32_t RESERVED1[120];
__IO uint32_t PIN_CNF[32]; /*!< Configuration of GPIO pins. */
} FL_NRF_GPIO_Type;
#define FL_NRF_GPIO_BASE 0x50000504UL
#define FL_NRF_GPIO ((FL_NRF_GPIO_Type *) FL_NRF_GPIO_BASE)
template<uint8_t PIN, uint32_t _MASK> class _ARMPIN {
public:
typedef volatile uint32_t * port_ptr_t;
typedef uint32_t port_t;
inline static void setOutput() { FL_NRF_GPIO->DIRSET = _MASK; }
inline static void setInput() { FL_NRF_GPIO->DIRCLR = _MASK; }
inline static void hi() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTSET = _MASK; }
inline static void lo() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTCLR= _MASK; }
inline static void set(register port_t val) __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT = val; }
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
inline static void toggle() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT ^= _MASK; }
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
inline static port_t hival() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT | _MASK; }
inline static port_t loval() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT & ~_MASK; }
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &FL_NRF_GPIO->OUT; }
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
inline static bool isset() __attribute__ ((always_inline)) { return (FL_NRF_GPIO->IN & _MASK) != 0; }
};
#define _FL_DEFPIN(PIN) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << PIN> {};
#endif
// Actual pin definitions
#define MAX_PIN 31
_FL_DEFPIN(0); _FL_DEFPIN(1); _FL_DEFPIN(2); _FL_DEFPIN(3);
_FL_DEFPIN(4); _FL_DEFPIN(5); _FL_DEFPIN(6); _FL_DEFPIN(7);
_FL_DEFPIN(8); _FL_DEFPIN(9); _FL_DEFPIN(10); _FL_DEFPIN(11);
_FL_DEFPIN(12); _FL_DEFPIN(13); _FL_DEFPIN(14); _FL_DEFPIN(15);
_FL_DEFPIN(16); _FL_DEFPIN(17); _FL_DEFPIN(18); _FL_DEFPIN(19);
_FL_DEFPIN(20); _FL_DEFPIN(21); _FL_DEFPIN(22); _FL_DEFPIN(23);
_FL_DEFPIN(24); _FL_DEFPIN(25); _FL_DEFPIN(26); _FL_DEFPIN(27);
_FL_DEFPIN(28); _FL_DEFPIN(29); _FL_DEFPIN(30); _FL_DEFPIN(31);
#define HAS_HARDWARE_PIN_SUPPORT
#endif
#endif

View File

@@ -0,0 +1,149 @@
#ifndef __INC_FASTSPI_NRF_H
#define __INC_FASTSPI_NRF_H
#ifdef NRF51
#ifndef FASTLED_FORCE_SOFTWARE_SPI
#define FASTLED_ALL_PINS_HARDWARE_SPI
// 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, uint32_t _SPI_CLOCK_DIVIDER>
class NRF51SPIOutput {
struct saveData {
uint32_t sck;
uint32_t mosi;
uint32_t miso;
uint32_t freq;
uint32_t enable;
} mSavedData;
void saveSPIData() {
mSavedData.sck = NRF_SPI0->PSELSCK;
mSavedData.mosi = NRF_SPI0->PSELMOSI;
mSavedData.miso = NRF_SPI0->PSELMISO;
mSavedData.freq = NRF_SPI0->FREQUENCY;
mSavedData.enable = NRF_SPI0->ENABLE;
}
void restoreSPIData() {
NRF_SPI0->PSELSCK = mSavedData.sck;
NRF_SPI0->PSELMOSI = mSavedData.mosi;
NRF_SPI0->PSELMISO = mSavedData.miso;
NRF_SPI0->FREQUENCY = mSavedData.freq;
mSavedData.enable = NRF_SPI0->ENABLE;
}
public:
NRF51SPIOutput() { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); }
NRF51SPIOutput(Selectable *pSelect) { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); }
// set the object representing the selectable
void setSelect(Selectable *pSelect) { /* TODO */ }
// initialize the SPI subssytem
void init() {
FastPin<_DATA_PIN>::setOutput();
FastPin<_CLOCK_PIN>::setOutput();
NRF_SPI0->PSELSCK = _CLOCK_PIN;
NRF_SPI0->PSELMOSI = _DATA_PIN;
NRF_SPI0->PSELMISO = 0xFFFFFFFF;
NRF_SPI0->FREQUENCY = 0x80000000;
NRF_SPI0->ENABLE = 1;
NRF_SPI0->EVENTS_READY = 0;
}
// latch the CS select
void select() { saveSPIData(); init(); }
// release the CS select
void release() { shouldWait(); restoreSPIData(); }
static bool shouldWait(bool wait = false) __attribute__((always_inline)) __attribute__((always_inline)) {
// static bool sWait=false;
// bool oldWait = sWait;
// sWait = wait;
// never going to bother with waiting since we're always running the spi clock at max speed on the rfduino
// TODO: When we set clock rate, implement/fix waiting properly, otherwise the world hangs up
return false;
}
// wait until all queued up data has been written
static void waitFully() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->INTENCLR; }
static void wait() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->INTENCLR; }
// write a byte out via SPI (returns immediately on writing register)
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); NRF_SPI0->TXD = b; NRF_SPI0->INTENCLR; shouldWait(true); }
// write a word out via SPI (returns immediately on writing register)
static void writeWord(uint16_t w) __attribute__((always_inline)){ writeByte(w>>8); writeByte(w & 0xFF); }
// A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
static void writeBytesValueRaw(uint8_t value, int len) { while(len--) { writeByte(value); } }
// A full cycle of writing a value for len bytes, including select, release, and waiting
void writeBytesValue(uint8_t value, int len) {
select();
while(len--) {
writeByte(value);
}
waitFully();
release();
}
// A full cycle of writing a raw block of data out, including select, release, and waiting
template<class D> void writeBytes(uint8_t *data, int len) {
uint8_t *end = data + len;
select();
while(data != end) {
writeByte(D::adjust(*data++));
}
D::postBlock(len);
waitFully();
release();
}
void writeBytes(uint8_t *data, int len) {
writeBytes<DATA_NOP>(data, len);
}
// write a single bit out, which bit from the passed in byte is determined by template parameter
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
waitFully();
NRF_SPI0->ENABLE = 0;
if(b & 1<<BIT) {
FastPin<_DATA_PIN>::hi();
} else {
FastPin<_DATA_PIN>::lo();
}
FastPin<_CLOCK_PIN>::toggle();
FastPin<_CLOCK_PIN>::toggle();
NRF_SPI0->ENABLE = 1;
}
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
select();
int len = pixels.mLen;
while(pixels.has(1)) {
if(FLAGS & FLAG_START_BIT) {
writeBit<0>(1);
}
writeByte(D::adjust(pixels.loadAndScale0()));
writeByte(D::adjust(pixels.loadAndScale1()));
writeByte(D::adjust(pixels.loadAndScale2()));
pixels.advanceData();
pixels.stepDithering();
}
D::postBlock(len);
waitFully();
release();
}
};
#endif
#endif
#endif

View File

@@ -0,0 +1,46 @@
#ifndef __LED_SYSDEFS_ARM_NRF51
#define __LED_SYSDEFS_ARM_NRF51
#ifndef NRF51
#define NRF51
#endif
#define LED_TIMER NRF_TIMER1
#define FASTLED_NO_PINMAP
#define FASTLED_HAS_CLOCKLESS
#define FASTLED_SPI_BYTE_ONLY
#define FASTLED_ARM
#define FASTLED_ARM_M0
#ifndef F_CPU
#define F_CPU 16000000
#endif
#include <stdint.h>
#include <nrf51.h>
#include <core_cm0.h>
typedef volatile uint32_t RoReg;
typedef volatile uint32_t RwReg;
typedef uint32_t prog_uint32_t;
typedef uint8_t boolean;
#define PROGMEM
#define NO_PROGMEM
#define NEED_CXX_BITS
// Default to NOT using PROGMEM here
#ifndef FASTLED_USE_PROGMEM
#define FASTLED_USE_PROGMEM 0
#endif
#ifndef FASTLED_ALLOW_INTERRUPTS
#define FASTLED_ALLOW_INTERRUPTS 1
#endif
#define cli() __disable_irq();
#define sei() __enable_irq();
#endif

View File

@@ -0,0 +1,114 @@
#ifndef __INC_ARBITER_NRF52
#define __INC_ARBITER_NRF52
#if defined(NRF52_SERIES)
#include "led_sysdefs_arm_nrf52.h"
//FASTLED_NAMESPACE_BEGIN
typedef void (*FASTLED_NRF52_PWM_INTERRUPT_HANDLER)();
// a trick learned from other embedded projects ..
// use the enum as an index to a statically-allocated array
// to store unique information for that instance.
// also provides a count of how many instances were enabled.
//
// See led_sysdefs_arm_nrf52.h for selection....
//
typedef enum _FASTLED_NRF52_ENABLED_PWM_INSTANCE {
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0)
FASTLED_NRF52_PWM0_INSTANCE_IDX,
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1)
FASTLED_NRF52_PWM1_INSTANCE_IDX,
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2)
FASTLED_NRF52_PWM2_INSTANCE_IDX,
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3)
FASTLED_NRF52_PWM3_INSTANCE_IDX,
#endif
FASTLED_NRF52_PWM_INSTANCE_COUNT
} FASTLED_NRF52_ENABLED_PWM_INSTANCES;
static_assert(FASTLED_NRF52_PWM_INSTANCE_COUNT > 0, "Instance count must be greater than zero -- define FASTLED_NRF52_ENABLE_PWM_INSTNACE[n] (replace `[n]` with digit)");
template <uint32_t _PWM_ID>
class PWM_Arbiter {
private:
static_assert(_PWM_ID < 32, "PWM_ID over 31 breaks current arbitration bitmask");
//const uint32_t _ACQUIRE_MASK = (1u << _PWM_ID) ;
//const uint32_t _CLEAR_MASK = ~((uint32_t)(1u << _PWM_ID));
static uint32_t s_PwmInUse;
static NRF_PWM_Type * const s_PWM;
static IRQn_Type const s_PWM_IRQ;
static FASTLED_NRF52_PWM_INTERRUPT_HANDLER volatile s_Isr;
public:
static void isr_handler() {
return s_Isr();
}
FASTLED_NRF52_INLINE_ATTRIBUTE static bool isAcquired() {
return (0u != (s_PwmInUse & 1u)); // _ACQUIRE_MASK
}
FASTLED_NRF52_INLINE_ATTRIBUTE static void acquire(FASTLED_NRF52_PWM_INTERRUPT_HANDLER isr) {
while (!tryAcquire(isr));
}
FASTLED_NRF52_INLINE_ATTRIBUTE static bool tryAcquire(FASTLED_NRF52_PWM_INTERRUPT_HANDLER isr) {
uint32_t oldValue = __sync_fetch_and_or(&s_PwmInUse, 1u); // _ACQUIRE_MASK
if (0u == (oldValue & 1u)) { // _ACQUIRE_MASK
s_Isr = isr;
return true;
}
return false;
}
FASTLED_NRF52_INLINE_ATTRIBUTE static void releaseFromIsr() {
uint32_t oldValue = __sync_fetch_and_and(&s_PwmInUse, ~1u); // _CLEAR_MASK
if (0u == (oldValue & 1u)) { // _ACQUIRE_MASK
// TODO: This should never be true... indicates was not held.
// Assert here?
(void)oldValue;
}
return;
}
FASTLED_NRF52_INLINE_ATTRIBUTE static NRF_PWM_Type * getPWM() {
return s_PWM;
}
FASTLED_NRF52_INLINE_ATTRIBUTE static IRQn_Type getIRQn() { return s_PWM_IRQ; }
};
template <uint32_t _PWM_ID> NRF_PWM_Type * const PWM_Arbiter<_PWM_ID>::s_PWM =
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0)
(_PWM_ID == 0 ? NRF_PWM0 :
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1)
(_PWM_ID == 1 ? NRF_PWM1 :
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2)
(_PWM_ID == 2 ? NRF_PWM2 :
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3)
(_PWM_ID == 3 ? NRF_PWM3 :
#endif
(NRF_PWM_Type*)-1
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0)
)
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1)
)
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2)
)
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3)
)
#endif
;
template <uint32_t _PWM_ID> IRQn_Type const PWM_Arbiter<_PWM_ID>::s_PWM_IRQ = ((IRQn_Type)((uint8_t)((uint32_t)(s_PWM) >> 12)));
template <uint32_t _PWM_ID> uint32_t PWM_Arbiter<_PWM_ID>::s_PwmInUse = 0;
template <uint32_t _PWM_ID> FASTLED_NRF52_PWM_INTERRUPT_HANDLER volatile PWM_Arbiter<_PWM_ID>::s_Isr = NULL;
//FASTLED_NAMESPACE_END
#endif // NRF52_SERIES
#endif // __INC_ARBITER_NRF52

View File

@@ -0,0 +1,390 @@
#ifndef __INC_CLOCKLESS_ARM_NRF52
#define __INC_CLOCKLESS_ARM_NRF52
#if defined(NRF52_SERIES)
//FASTLED_NAMESPACE_BEGIN
#define FASTLED_HAS_CLOCKLESS 1
#define FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING 144 // TODO: Figure out how to safely let this be calller-defined....
// nRF52810 has a single PWM peripheral (PWM0)
// nRF52832 has three PWM peripherals (PWM0, PWM1, PWM2)
// nRF52840 has four PWM peripherals (PWM0, PWM1, PWM2, PWM3)
// NOTE: Update platforms.cpp in root of FastLED library if this changes
#define FASTLED_NRF52_PWM_ID 0
extern uint32_t isrCount;
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER = RGB, int _XTRA0 = 0, bool _FLIP = false, int _WAIT_TIME_MICROSECONDS = 10>
class ClocklessController : public CPixelLEDController<_RGB_ORDER> {
static_assert(FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING > 0, "Maximum string length must be positive value (FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING)");
static_assert(_T1 > 0 , "negative values are not allowed");
static_assert(_T2 > 0 , "negative values are not allowed");
static_assert(_T3 > 0 , "negative values are not allowed");
static_assert(_T1 < (0x8000u-2u), "_T1 must fit in 15 bits");
static_assert(_T2 < (0x8000u-2u), "_T2 must fit in 15 bits");
static_assert(_T3 < (0x8000u-2u), "_T3 must fit in 15 bits");
static_assert(_T1 < (0x8000u-2u), "_T0H must fit in 15 bits");
static_assert(_T1+_T2 < (0x8000u-2u), "_T1H must fit in 15 bits");
static_assert(_T1+_T2+_T3 < (0x8000u-2u), "_TOP must fit in 15 bits");
static_assert(_T1+_T2+_T3 <= PWM_COUNTERTOP_COUNTERTOP_Msk, "_TOP too large for peripheral");
private:
static const bool _INITIALIZE_PIN_HIGH = (_FLIP ? 1 : 0);
static const uint16_t _POLARITY_BIT = (_FLIP ? 0 : 0x8000);
static const uint8_t _BITS_PER_PIXEL = (8 + _XTRA0) * 3; // NOTE: 3 means RGB only...
static const uint16_t _PWM_BUFFER_COUNT = (_BITS_PER_PIXEL * FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING);
static const uint8_t _T0H = ((uint16_t)(_T1 ));
static const uint8_t _T1H = ((uint16_t)(_T1+_T2 ));
static const uint8_t _TOP = ((uint16_t)(_T1+_T2+_T3));
// may as well be static, as can only attach one LED string per _DATA_PIN....
static uint16_t s_SequenceBuffer[_PWM_BUFFER_COUNT];
static uint16_t s_SequenceBufferValidElements;
static volatile uint32_t s_SequenceBufferInUse;
static CMinWait<_WAIT_TIME_MICROSECONDS> mWait; // ensure data has time to latch
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_InitializePinState() {
FastPin<_DATA_PIN>::setOutput();
if (_INITIALIZE_PIN_HIGH) {
FastPin<_DATA_PIN>::hi();
} else {
FastPin<_DATA_PIN>::lo();
}
}
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_InitializePwmInstance(NRF_PWM_Type * pwm) {
// Pins must be set before enabling the peripheral
pwm->PSEL.OUT[0] = FastPin<_DATA_PIN>::nrf_pin();
pwm->PSEL.OUT[1] = NRF_PWM_PIN_NOT_CONNECTED;
pwm->PSEL.OUT[2] = NRF_PWM_PIN_NOT_CONNECTED;
pwm->PSEL.OUT[3] = NRF_PWM_PIN_NOT_CONNECTED;
nrf_pwm_enable(pwm);
nrf_pwm_configure(pwm, NRF_PWM_CLK_16MHz, NRF_PWM_MODE_UP, _TOP);
nrf_pwm_decoder_set(pwm, NRF_PWM_LOAD_COMMON, NRF_PWM_STEP_AUTO);
// clear any prior shorts / interrupt enable bits
nrf_pwm_shorts_set(pwm, 0);
nrf_pwm_int_set(pwm, 0);
// clear all prior events
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_STOPPED);
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQSTARTED0);
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQSTARTED1);
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQEND0);
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQEND1);
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_PWMPERIODEND);
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_LOOPSDONE);
}
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_ConfigurePwmSequence(NRF_PWM_Type * pwm) {
// config is easy, using SEQ0, no loops...
nrf_pwm_sequence_t sequenceConfig;
sequenceConfig.values.p_common = &(s_SequenceBuffer[0]);
sequenceConfig.length = s_SequenceBufferValidElements;
sequenceConfig.repeats = 0; // send the data once, and only once
sequenceConfig.end_delay = 0; // no extra delay at the end of SEQ[0] / SEQ[1]
nrf_pwm_sequence_set(pwm, 0, &sequenceConfig);
nrf_pwm_sequence_set(pwm, 1, &sequenceConfig);
nrf_pwm_loop_set(pwm, 0);
}
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_EnableInterruptsAndShortcuts(NRF_PWM_Type * pwm) {
IRQn_Type irqn = PWM_Arbiter<FASTLED_NRF52_PWM_ID>::getIRQn();
// TODO: check API results...
uint32_t result;
result = sd_nvic_SetPriority(irqn, configMAX_SYSCALL_INTERRUPT_PRIORITY);
(void)result;
result = sd_nvic_EnableIRQ(irqn);
(void)result;
// shortcuts prevent (up to) 4-cycle delay from interrupt handler to next action
uint32_t shortsToEnable = 0;
shortsToEnable |= NRF_PWM_SHORT_SEQEND0_STOP_MASK; ///< SEQEND[0] --> STOP task.
shortsToEnable |= NRF_PWM_SHORT_SEQEND1_STOP_MASK; ///< SEQEND[1] --> STOP task.
//shortsToEnable |= NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK; ///< LOOPSDONE --> SEQSTART[0] task.
//shortsToEnable |= NRF_PWM_SHORT_LOOPSDONE_SEQSTART1_MASK; ///< LOOPSDONE --> SEQSTART[1] task.
shortsToEnable |= NRF_PWM_SHORT_LOOPSDONE_STOP_MASK; ///< LOOPSDONE --> STOP task.
nrf_pwm_shorts_set(pwm, shortsToEnable);
// mark which events should cause interrupts...
uint32_t interruptsToEnable = 0;
interruptsToEnable |= NRF_PWM_INT_SEQEND0_MASK;
interruptsToEnable |= NRF_PWM_INT_SEQEND1_MASK;
interruptsToEnable |= NRF_PWM_INT_LOOPSDONE_MASK;
interruptsToEnable |= NRF_PWM_INT_STOPPED_MASK;
nrf_pwm_int_set(pwm, interruptsToEnable);
}
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_StartTask(NRF_PWM_Type * pwm) {
nrf_pwm_task_trigger(pwm, NRF_PWM_TASK_SEQSTART0);
}
FASTLED_NRF52_INLINE_ATTRIBUTE static void spinAcquireSequenceBuffer() {
while (!tryAcquireSequenceBuffer());
}
FASTLED_NRF52_INLINE_ATTRIBUTE static bool tryAcquireSequenceBuffer() {
return __sync_bool_compare_and_swap(&s_SequenceBufferInUse, 0, 1);
}
FASTLED_NRF52_INLINE_ATTRIBUTE static void releaseSequenceBuffer() {
uint32_t tmp = __sync_val_compare_and_swap(&s_SequenceBufferInUse, 1, 0);
if (tmp != 1) {
// TODO: Error / Assert / log ?
}
}
public:
static void isr_handler() {
NRF_PWM_Type * pwm = PWM_Arbiter<FASTLED_NRF52_PWM_ID>::getPWM();
IRQn_Type irqn = PWM_Arbiter<FASTLED_NRF52_PWM_ID>::getIRQn();
// Currently, only use SEQUENCE 0, so only event
// of consequence is LOOPSDONE ...
if (nrf_pwm_event_check(pwm,NRF_PWM_EVENT_STOPPED)) {
nrf_pwm_event_clear(pwm,NRF_PWM_EVENT_STOPPED);
// update the minimum time to next call
mWait.mark();
// mark the sequence as no longer in use -- pointer, comparator, exchange value
releaseSequenceBuffer();
// prevent further interrupts from PWM events
nrf_pwm_int_set(pwm, 0);
// disable PWM interrupts - None of the PWM IRQs are shared
// with other peripherals, avoiding complexity of shared IRQs.
sd_nvic_DisableIRQ(irqn);
// disable the PWM instance
nrf_pwm_disable(pwm);
// may take up to 4 cycles for writes to propagate (APB bus @ 16MHz)
asm __volatile__ ( "NOP; NOP; NOP; NOP;" );
// release the PWM arbiter to be re-used by another LED string
PWM_Arbiter<FASTLED_NRF52_PWM_ID>::releaseFromIsr();
}
}
virtual void init() {
FASTLED_NRF52_DEBUGPRINT("Clockless Timings:\n");
FASTLED_NRF52_DEBUGPRINT(" T0H == %d", _T0H);
FASTLED_NRF52_DEBUGPRINT(" T1H == %d", _T1H);
FASTLED_NRF52_DEBUGPRINT(" TOP == %d\n", _TOP);
// to avoid pin initialization from causing first LED to have invalid color,
// call mWait.mark() to ensure data latches before color data gets sent.
startPwmPlayback_InitializePinState();
mWait.mark();
}
virtual uint16_t getMaxRefreshRate() const { return 800; }
virtual void showPixels(PixelController<_RGB_ORDER> & pixels) {
// wait for the only sequence buffer to become available
spinAcquireSequenceBuffer();
prepareSequenceBuffers(pixels);
// ensure any prior data had time to latch
mWait.wait();
startPwmPlayback(s_SequenceBufferValidElements);
return;
}
template<uint8_t _BIT>
FASTLED_NRF52_INLINE_ATTRIBUTE static void WriteBitToSequence(uint8_t byte, uint16_t * e) {
*e = _POLARITY_BIT | (((byte & (1u << _BIT)) == 0) ? _T0H : _T1H);
}
FASTLED_NRF52_INLINE_ATTRIBUTE static void prepareSequenceBuffers(PixelController<_RGB_ORDER> & pixels) {
s_SequenceBufferValidElements = 0;
int32_t remainingSequenceElements = _PWM_BUFFER_COUNT;
uint16_t * e = s_SequenceBuffer;
uint32_t size_needed = pixels.size(); // count of pixels
size_needed *= (8 + _XTRA0); // bits per pixel
size_needed *= 2; // each bit takes two bytes
if (size_needed > _PWM_BUFFER_COUNT) {
// TODO: assert()?
return;
}
while (pixels.has(1) && (remainingSequenceElements >= _BITS_PER_PIXEL)) {
uint8_t b0 = pixels.loadAndScale0();
WriteBitToSequence<7>(b0, e); ++e;
WriteBitToSequence<6>(b0, e); ++e;
WriteBitToSequence<5>(b0, e); ++e;
WriteBitToSequence<4>(b0, e); ++e;
WriteBitToSequence<3>(b0, e); ++e;
WriteBitToSequence<2>(b0, e); ++e;
WriteBitToSequence<1>(b0, e); ++e;
WriteBitToSequence<0>(b0, e); ++e;
if (_XTRA0 > 0) {
for (int i = 0; i < _XTRA0; ++i) {
WriteBitToSequence<0>(0,e); ++e;
}
}
uint8_t b1 = pixels.loadAndScale1();
WriteBitToSequence<7>(b1, e); ++e;
WriteBitToSequence<6>(b1, e); ++e;
WriteBitToSequence<5>(b1, e); ++e;
WriteBitToSequence<4>(b1, e); ++e;
WriteBitToSequence<3>(b1, e); ++e;
WriteBitToSequence<2>(b1, e); ++e;
WriteBitToSequence<1>(b1, e); ++e;
WriteBitToSequence<0>(b1, e); ++e;
if (_XTRA0 > 0) {
for (int i = 0; i < _XTRA0; ++i) {
WriteBitToSequence<0>(0,e); ++e;
}
}
uint8_t b2 = pixels.loadAndScale2();
WriteBitToSequence<7>(b2, e); ++e;
WriteBitToSequence<6>(b2, e); ++e;
WriteBitToSequence<5>(b2, e); ++e;
WriteBitToSequence<4>(b2, e); ++e;
WriteBitToSequence<3>(b2, e); ++e;
WriteBitToSequence<2>(b2, e); ++e;
WriteBitToSequence<1>(b2, e); ++e;
WriteBitToSequence<0>(b2, e); ++e;
if (_XTRA0 > 0) {
for (int i = 0; i < _XTRA0; ++i) {
WriteBitToSequence<0>(0,e); ++e;
}
}
// advance pixel and sequence pointers
s_SequenceBufferValidElements += _BITS_PER_PIXEL;
remainingSequenceElements -= _BITS_PER_PIXEL;
pixels.advanceData();
pixels.stepDithering();
}
}
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback(uint16_t bytesToSend) {
PWM_Arbiter<FASTLED_NRF52_PWM_ID>::acquire(isr_handler);
NRF_PWM_Type * pwm = PWM_Arbiter<FASTLED_NRF52_PWM_ID>::getPWM();
// mark the sequence as being in-use
__sync_fetch_and_or(&s_SequenceBufferInUse, 1);
startPwmPlayback_InitializePinState();
startPwmPlayback_InitializePwmInstance(pwm);
startPwmPlayback_ConfigurePwmSequence(pwm);
startPwmPlayback_EnableInterruptsAndShortcuts(pwm);
startPwmPlayback_StartTask(pwm);
return;
}
#if 0
FASTLED_NRF52_INLINE_ATTRIBUTE static uint16_t* getRawSequenceBuffer() { return s_SequenceBuffer; }
FASTLED_NRF52_INLINE_ATTRIBUTE static uint16_t getRawSequenceBufferSize() { return _PWM_BUFFER_COUNT; }
FASTLED_NRF52_INLINE_ATTRIBUTE static uint16_t getSequenceBufferInUse() { return s_SequenceBufferInUse; }
FASTLED_NRF52_INLINE_ATTRIBUTE static void sendRawSequenceBuffer(uint16_t bytesToSend) {
mWait.wait(); // ensure min time between updates
startPwmPlayback(bytesToSend);
}
FASTLED_NRF52_INLINE_ATTRIBUTE static void sendRawBytes(uint8_t * arrayOfBytes, uint16_t bytesToSend) {
// wait for sequence buffer to be available
while (s_SequenceBufferInUse != 0);
s_SequenceBufferValidElements = 0;
int32_t remainingSequenceElements = _PWM_BUFFER_COUNT;
uint16_t * e = s_SequenceBuffer;
uint8_t * nextByte = arrayOfBytes;
for (uint16_t bytesRemain = bytesToSend;
(remainingSequenceElements >= 8) && (bytesRemain > 0);
--bytesRemain,
remainingSequenceElements -= 8,
s_SequenceBufferValidElements += 8
) {
uint8_t b = *nextByte;
WriteBitToSequence<7,false>(b, e); ++e;
WriteBitToSequence<6,false>(b, e); ++e;
WriteBitToSequence<5,false>(b, e); ++e;
WriteBitToSequence<4,false>(b, e); ++e;
WriteBitToSequence<3,false>(b, e); ++e;
WriteBitToSequence<2,false>(b, e); ++e;
WriteBitToSequence<1,false>(b, e); ++e;
WriteBitToSequence<0,false>(b, e); ++e;
if (_XTRA0 > 0) {
for (int i = 0; i < _XTRA0; ++i) {
WriteBitToSequence<0,_FLIP>(0,e); ++e;
}
}
}
mWait.wait(); // ensure min time between updates
startPwmPlayback(s_SequenceBufferValidElements);
}
#endif // 0
};
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER, int _XTRA0, bool _FLIP, int _WAIT_TIME_MICROSECONDS>
uint16_t ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::s_SequenceBufferValidElements = 0;
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER, int _XTRA0, bool _FLIP, int _WAIT_TIME_MICROSECONDS>
uint32_t volatile ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::s_SequenceBufferInUse = 0;
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER, int _XTRA0, bool _FLIP, int _WAIT_TIME_MICROSECONDS>
uint16_t ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::s_SequenceBuffer[_PWM_BUFFER_COUNT];
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER, int _XTRA0, bool _FLIP, int _WAIT_TIME_MICROSECONDS>
CMinWait<_WAIT_TIME_MICROSECONDS> ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::mWait;
/* nrf_pwm solution
//
// When the nRF52 softdevice (e.g., BLE) is enabled, the CPU can be pre-empted
// at any time for radio interrupts. These interrupts cannot be disabled.
// The problem is, even simple BLE advertising interrupts may take **`348μs`**
// (per softdevice 1.40, see http://infocenter.nordicsemi.com/pdf/S140_SDS_v1.3.pdf)
//
// The nRF52 chips have a decent Easy-DMA-enabled PWM peripheral.
//
// The major downside:
// [] The PWM peripheral has a fixed input buffer size at 16 bits per clock cycle.
// (each clockless protocol bit == 2 bytes)
//
// The major upsides include:
// [] Fully asynchronous, freeing CPU for other tasks
// [] Softdevice interrupts do not affect PWM clocked output (reliable clocking)
//
// The initial solution generally does the following for showPixels():
// [] wait for a sequence buffer to become available
// [] prepare the entire LED string's sequence (see `prepareSequenceBuffers()`)
// [] ensures minimum wait time from prior sequence's end
//
// Options after initial solution working:
// []
// TODO: Double-buffers, so one can be doing DMA while the second
// buffer is being prepared.
// TODO: Pool of buffers, so can keep N-1 active in DMA, while
// preparing data in the final buffer?
// Write another class similar to PWM_Arbiter, only for
// tracking use of sequence buffers?
// TODO: Use volatile variable to track buffers that the
// prior DMA operation is finished with, so can fill
// in those buffers with newly-prepared data...
// apis to send the pre-generated buffer. This would be essentially asynchronous,
// and result in efficient run time if the pixels are either (a) static, or
// (b) cycle through a limited number of options whose converted results can
// be cached and re-used. While simple, this method takes lots of extra RAM...
// 16 bits for every full clock (high/low) cycle.
//
// Clockless chips typically send 24 bits (3x 8-bit) per pixel.
// One odd clockless chip sends 36 bits (3x 12-bit) per pixel.
// Each bit requires a 16-bit sequence entry for the PWM peripheral.
// This gives approximately:
// 24 bpp 36 bpp
// ==========================================
// 1 pixel 48 bytes 72 bytes
// 32 pixels 1,536 bytes 2,304 bytes
// 64 pixels 3,072 bytes 4,608 bytes
//
//
// UPDATE: this is the method I'm choosing, to get _SOMETHING_
// clockless working... 3k RAM for 64 pixels is acceptable
// for a first release, as it allows re-use of FASTLED
// color correction, dithering, etc. ....
*/
//FASTLED_NAMESPACE_END
#endif // NRF52_SERIES
#endif // __INC_CLOCKLESS_ARM_NRF52

View File

@@ -0,0 +1,11 @@
#ifndef __INC_FASTLED_ARM_NRF52_H
#define __INC_FASTLED_ARM_NRF52_H
#include "led_sysdefs_arm_nrf52.h"
#include "arbiter_nrf52.h"
#include "fastpin_arm_nrf52.h"
#include "fastspi_arm_nrf52.h"
#include "clockless_arm_nrf52.h"
#endif // #ifndef __INC_FASTLED_ARM_NRF52_H

View File

@@ -0,0 +1,190 @@
#ifndef __FASTPIN_ARM_NRF52_H
#define __FASTPIN_ARM_NRF52_H
/*
//
// Background:
// ===========
// the nRF52 has more than 32 ports, and thus must support
// two distinct GPIO port registers.
//
// For the nRF52 series, the structure to control the port is
// `NRF_GPIO_Type`, with separate addresses mapped for set, clear, etc.
// The two ports are defined as NRF_P0 and NRF_P1.
// An example declaration for the ports is:
// #define NRF_P0_BASE 0x50000000UL
// #define NRF_P1_BASE 0x50000300UL
// #define NRF_P0 ((NRF_GPIO_Type*)NRF_P0_BASE)
// #define NRF_P1 ((NRF_GPIO_Type*)NRF_P1_BASE)
//
// Therefore, ideally, the _FL_DEFPIN() macro would simply
// conditionally pass either NRF_P0 or NRF_P1 to the underlying
// FastPin<> template class class.
//
// The "pin" provided to the FastLED<> template (and which
// the _FL_DEFPIN() macro specializes for valid pins) is NOT
// the microcontroller port.pin, but the Arduino digital pin.
// Some boards have an identity mapping (e.g., nRF52832 Feather)
// but most do not. Therefore, the _FL_DEFPIN() macro
// must translate the Arduino pin to the mcu port.pin.
//
//
// Difficulties:
// =============
// The goal is to avoid any such lookups, using compile-time
// optimized functions for speed, in line with FastLED's
// overall design goals. This means constexpr, compile-time
// and aggressive inlining of functions....
//
// Right away, this precludes the use of g_ADigitalPinMap,
// which is not constexpr, and thus not available for
// preprocessor/compile-time optimizations. Therefore,
// we have to specialize FastPin<uint8_t PIN>, given a
// compile-time value for PIN, into at least a PORT and
// a BITMASK for the port.
//
// Arduino compiles using C++11 for at least Feather nRF52840 Express.
// C++11 is very restrictive about template parameters.
// Template parameters can only be:
// 1. a type (as most people expect)
// 2. a template
// 3. a constexpr native integer type
//
// Therefore, attempts to use `NRF_GPIO_Type *` as a
// template parameter will fail....
//
// Solution:
// =========
// The solution chosen is to define a unique structure for each port,
// whose SOLE purpose is to have a static inline function that
// returns the `NRF_GPIO_Type *` that is needed.
//
// Thus, while it's illegal to pass `NRF_P0` as a template
// parameter, it's perfectly legal to pass `__generated_struct_NRF_P0`,
// and have the template call a well-known `static inline` function
// that returns `NRF_P0` ... which is itself a compile-time constant.
//
// Note that additional magic can be applied that will automatically
// generate the structures. If you want to add that to this platform,
// check out the KL26 platform files for a starting point.
//
*/
// manually define two structures, to avoid fighting with preprocessor macros
struct __generated_struct_NRF_P0 {
FASTLED_NRF52_INLINE_ATTRIBUTE constexpr static uintptr_t r() {
return NRF_P0_BASE;
}
};
// Not all NRF52 chips have two ports. Only define if P1 is present.
#if defined(NRF_P1_BASE)
struct __generated_struct_NRF_P1 {
FASTLED_NRF52_INLINE_ATTRIBUTE constexpr static uintptr_t r() {
return NRF_P1_BASE;
}
};
#endif
// The actual class template can then use a typename, for what is essentially a constexpr NRF_GPIO_Type*
template <uint32_t _MASK, typename _PORT, uint8_t _PORT_NUMBER, uint8_t _PIN_NUMBER> class _ARMPIN {
public:
typedef volatile uint32_t * port_ptr_t;
typedef uint32_t port_t;
FASTLED_NRF52_INLINE_ATTRIBUTE static void setOutput() {
// OK for this to be more than one instruction, as unusual to quickly switch input/output modes
nrf_gpio_cfg(
nrf_pin(),
NRF_GPIO_PIN_DIR_OUTPUT, // set pin as output
NRF_GPIO_PIN_INPUT_DISCONNECT, // disconnect the input buffering
NRF_GPIO_PIN_NOPULL, // neither pull-up nor pull-down resistors enabled
NRF_GPIO_PIN_H0H1, // high drive mode required for faster speeds
NRF_GPIO_PIN_NOSENSE // pin sense level disabled
);
}
FASTLED_NRF52_INLINE_ATTRIBUTE static void setInput() {
// OK for this to be more than one instruction, as unusual to quickly switch input/output modes
nrf_gpio_cfg(
nrf_pin(),
NRF_GPIO_PIN_DIR_INPUT, // set pin as input
NRF_GPIO_PIN_INPUT_DISCONNECT, // disconnect the input buffering
NRF_GPIO_PIN_NOPULL, // neither pull-up nor pull-down resistors enabled
NRF_GPIO_PIN_H0H1, // high drive mode required for faster speeds
NRF_GPIO_PIN_NOSENSE // pin sense level disabled
);
}
FASTLED_NRF52_INLINE_ATTRIBUTE static void hi() { (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUTSET = _MASK; } // sets _MASK in the SET OUTPUT register (output set high)
FASTLED_NRF52_INLINE_ATTRIBUTE static void lo() { (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUTCLR = _MASK; } // sets _MASK in the CLEAR OUTPUT register (output set low)
FASTLED_NRF52_INLINE_ATTRIBUTE static void toggle() { (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT ^= _MASK; } // toggles _MASK bits in the OUTPUT GPIO port directly
FASTLED_NRF52_INLINE_ATTRIBUTE static void strobe() { toggle(); toggle(); } // BUGBUG -- Is this used by FastLED? Without knowing (for example) SPI Speed?
FASTLED_NRF52_INLINE_ATTRIBUTE static port_t hival() { return (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT | _MASK; } // sets all _MASK bit(s) in the OUTPUT GPIO port to 1
FASTLED_NRF52_INLINE_ATTRIBUTE static port_t loval() { return (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT & ~_MASK; } // sets all _MASK bit(s) in the OUTPUT GPIO port to 0
FASTLED_NRF52_INLINE_ATTRIBUTE static port_ptr_t port() { return &((reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT); } // gets raw pointer to OUTPUT GPIO port
FASTLED_NRF52_INLINE_ATTRIBUTE static port_ptr_t cport() { return &((reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUTCLR); } // gets raw pointer to SET DIRECTION GPIO port
FASTLED_NRF52_INLINE_ATTRIBUTE static port_ptr_t sport() { return &((reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUTSET); } // gets raw pointer to CLEAR DIRECTION GPIO port
FASTLED_NRF52_INLINE_ATTRIBUTE static port_t mask() { return _MASK; } // gets the value of _MASK
FASTLED_NRF52_INLINE_ATTRIBUTE static void hi (register port_ptr_t port) { hi(); } // sets _MASK in the SET OUTPUT register (output set high)
FASTLED_NRF52_INLINE_ATTRIBUTE static void lo (register port_ptr_t port) { lo(); } // sets _MASK in the CLEAR OUTPUT register (output set low)
FASTLED_NRF52_INLINE_ATTRIBUTE static void set(register port_t val ) { (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT = val; } // sets entire port's value (optimization used by FastLED)
FASTLED_NRF52_INLINE_ATTRIBUTE static void fastset(register port_ptr_t port, register port_t val) { *port = val; }
constexpr static uint32_t nrf_pin2() { return NRF_GPIO_PIN_MAP(_PORT_NUMBER, _PIN_NUMBER); }
constexpr static bool LowSpeedOnlyRecommended() {
// Caller must always determine if high speed use if allowed on a given pin,
// because it depends on more than just the chip packaging ... it depends on entire board (and even system) design.
return false; // choosing default to be FALSE, to allow users to ATTEMPT to use high-speed on pins where support is not known
}
// Expose the nrf pin (port/pin combined), port, and pin as properties (e.g., for setting up SPI)
FASTLED_NRF52_INLINE_ATTRIBUTE static uint32_t nrf_pin() { return NRF_GPIO_PIN_MAP(_PORT_NUMBER, _PIN_NUMBER); }
};
//
// BOARD_PIN can be either the pin portion of a port.pin, or the combined NRF_GPIO_PIN_MAP() number.
// For example both the following two defines refer to P1.15 (pin 47) as Arduino pin 3:
// _FL_DEFPIN(3, 15, 1);
// _FL_DEFPIN(3, 47, 1);
//
// Similarly, the following defines are all equivalent:
// _DEFPIN_ARM_IDENTITY_P1(47);
// _FL_DEFPIN(47, 15, 1);
// _FL_DEFPIN(47, 47, 1);
//
#define _FL_DEFPIN(ARDUINO_PIN, BOARD_PIN, BOARD_PORT) \
template<> class FastPin<ARDUINO_PIN> : \
public _ARMPIN< \
1u << (BOARD_PIN & 31u), \
__generated_struct_NRF_P ## BOARD_PORT, \
(BOARD_PIN / 32), \
BOARD_PIN & 31u \
> \
{}
#define _DEFPIN_ARM_IDENTITY_P0(ARDUINO_PIN) \
template<> class FastPin<ARDUINO_PIN> : \
public _ARMPIN< \
1u << (ARDUINO_PIN & 31u), \
__generated_struct_NRF_P0, \
0, \
(ARDUINO_PIN & 31u) + 0 \
> \
{}
#define _DEFPIN_ARM_IDENTITY_P1(ARDUINO_PIN) \
template<> class FastPin<ARDUINO_PIN> : \
public _ARMPIN< \
1u << (ARDUINO_PIN & 31u), \
__generated_struct_NRF_P1, \
1, \
(ARDUINO_PIN & 31u) + 32 \
> \
{}
// The actual pin definitions are in a separate header file...
#include "fastpin_arm_nrf52_variants.h"
#define HAS_HARDWARE_PIN_SUPPORT
#endif // #ifndef __FASTPIN_ARM_NRF52_H

View File

@@ -0,0 +1,723 @@
#ifndef __FASTPIN_ARM_NRF52_VARIANTS_H
#define __FASTPIN_ARM_NRF52_VARIANTS_H
// use this to determine if found variant or not (avoid multiple boards at once)
#undef __FASTPIN_ARM_NRF52_VARIANT_FOUND
// Adafruit Bluefruit nRF52832 Feather
// From https://www.adafruit.com/package_adafruit_index.json
#if defined (ARDUINO_NRF52832_FEATHER)
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
#warning "Adafruit Bluefruit nRF52832 Feather is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
#endif
_DEFPIN_ARM_IDENTITY_P0( 0); // xtal 1
_DEFPIN_ARM_IDENTITY_P0( 1); // xtal 2
_DEFPIN_ARM_IDENTITY_P0( 2); // a0
_DEFPIN_ARM_IDENTITY_P0( 3); // a1
_DEFPIN_ARM_IDENTITY_P0( 4); // a2
_DEFPIN_ARM_IDENTITY_P0( 5); // a3
_DEFPIN_ARM_IDENTITY_P0( 6); // TXD
_DEFPIN_ARM_IDENTITY_P0( 7); // GPIO #7
_DEFPIN_ARM_IDENTITY_P0( 8); // RXD
_DEFPIN_ARM_IDENTITY_P0( 9); // NFC1
_DEFPIN_ARM_IDENTITY_P0(10); // NFC2
_DEFPIN_ARM_IDENTITY_P0(11); // GPIO #11
_DEFPIN_ARM_IDENTITY_P0(12); // SCK
_DEFPIN_ARM_IDENTITY_P0(13); // MOSI
_DEFPIN_ARM_IDENTITY_P0(14); // MISO
_DEFPIN_ARM_IDENTITY_P0(15); // GPIO #15
_DEFPIN_ARM_IDENTITY_P0(16); // GPIO #16
_DEFPIN_ARM_IDENTITY_P0(17); // LED #1 (red)
_DEFPIN_ARM_IDENTITY_P0(18); // SWO
_DEFPIN_ARM_IDENTITY_P0(19); // LED #2 (blue)
_DEFPIN_ARM_IDENTITY_P0(20); // DFU
// _DEFPIN_ARM_IDENTITY_P0(21); // Reset -- not valid to use for FastLED?
// _DEFPIN_ARM_IDENTITY_P0(22); // Factory Reset -- not vaild to use for FastLED?
// _DEFPIN_ARM_IDENTITY_P0(23); // N/A
// _DEFPIN_ARM_IDENTITY_P0(24); // N/A
_DEFPIN_ARM_IDENTITY_P0(25); // SDA
_DEFPIN_ARM_IDENTITY_P0(26); // SCL
_DEFPIN_ARM_IDENTITY_P0(27); // GPIO #27
_DEFPIN_ARM_IDENTITY_P0(28); // A4
_DEFPIN_ARM_IDENTITY_P0(29); // A5
_DEFPIN_ARM_IDENTITY_P0(30); // A6
_DEFPIN_ARM_IDENTITY_P0(31); // A7
#endif // defined (ARDUINO_NRF52832_FEATHER)
// Adafruit Circuit Playground Bluefruit
// From https://www.adafruit.com/package_adafruit_index.json
#if defined (ARDUINO_NRF52840_CIRCUITPLAY)
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
// This board is a bit of a mess ... as it defines
// multiple arduino pins to map to a single Port/Pin
// combination.
// Use PIN_NEOPIXEL (D8) for the ten built-in neopixels
_FL_DEFPIN( 8, 13, 0); // P0.13 -- D8 / Neopixels
// PIN_A0 is connect to an amplifier, and thus *might*
// not be suitable for use with FastLED.
// Do not enable this pin until can confirm
// signal integrity from this pin.
//
// NOTE: it might also be possible if first disable
// the amp using D11 ("speaker shutdown" pin)
//
// _FL_DEFPIN(14, 26, 0); // P0.26 -- A0 / D12 / Audio Out
_FL_DEFPIN(15, 2, 0); // P0.02 -- A1 / D6
_FL_DEFPIN(16, 29, 0); // P0.29 -- A2 / D9
_FL_DEFPIN(17, 3, 0); // P0.03 -- A3 / D10
_FL_DEFPIN(18, 4, 0); // P0.04 -- A4 / D3 / SCL
_FL_DEFPIN(19, 5, 0); // P0.05 -- A5 / D2 / SDA
_FL_DEFPIN(20, 30, 0); // P0.30 -- A6 / D0 / UART RX
_FL_DEFPIN(21, 14, 0); // P0.14 -- AREF / D1 / UART TX
#endif
// Adafruit Bluefruit nRF52840 Feather Express
// From https://www.adafruit.com/package_adafruit_index.json
#if defined (ARDUINO_NRF52840_FEATHER)
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
// Arduino pins 0..7
_FL_DEFPIN( 0, 25, 0); // D0 is P0.25 -- UART TX
//_FL_DEFPIN( 1, 24, 0); // D1 is P0.24 -- UART RX
_FL_DEFPIN( 2, 10, 0); // D2 is P0.10 -- NFC2
_FL_DEFPIN( 3, 47, 1); // D3 is P1.15 -- PIN_LED1 (red)
_FL_DEFPIN( 4, 42, 1); // D4 is P1.10 -- PIN_LED2 (blue)
_FL_DEFPIN( 5, 40, 1); // D5 is P1.08 -- SPI/SS
_FL_DEFPIN( 6, 7, 0); // D6 is P0.07
_FL_DEFPIN( 7, 34, 1); // D7 is P1.02 -- PIN_DFU (Button)
// Arduino pins 8..15
_FL_DEFPIN( 8, 16, 0); // D8 is P0.16 -- PIN_NEOPIXEL
_FL_DEFPIN( 9, 26, 0); // D9 is P0.26
_FL_DEFPIN(10, 27, 0); // D10 is P0.27
_FL_DEFPIN(11, 6, 0); // D11 is P0.06
_FL_DEFPIN(12, 8, 0); // D12 is P0.08
_FL_DEFPIN(13, 41, 1); // D13 is P1.09
_FL_DEFPIN(14, 4, 0); // D14 is P0.04 -- A0
_FL_DEFPIN(15, 5, 0); // D15 is P0.05 -- A1
// Arduino pins 16..23
_FL_DEFPIN(16, 30, 0); // D16 is P0.30 -- A2
_FL_DEFPIN(17, 28, 0); // D17 is P0.28 -- A3
_FL_DEFPIN(18, 2, 0); // D18 is P0.02 -- A4
_FL_DEFPIN(19, 3, 0); // D19 is P0.03 -- A5
//_FL_DEFPIN(20, 29, 0); // D20 is P0.29 -- A6 -- Connected to battery!
//_FL_DEFPIN(21, 31, 0); // D21 is P0.31 -- A7 -- AREF
_FL_DEFPIN(22, 12, 0); // D22 is P0.12 -- SDA
_FL_DEFPIN(23, 11, 0); // D23 is P0.11 -- SCL
// Arduino pins 24..31
_FL_DEFPIN(24, 15, 0); // D24 is P0.15 -- PIN_SPI_MISO
_FL_DEFPIN(25, 13, 0); // D25 is P0.13 -- PIN_SPI_MOSI
_FL_DEFPIN(26, 14, 0); // D26 is P0.14 -- PIN_SPI_SCK
//_FL_DEFPIN(27, 19, 0); // D27 is P0.19 -- PIN_QSPI_SCK
//_FL_DEFPIN(28, 20, 0); // D28 is P0.20 -- PIN_QSPI_CS
//_FL_DEFPIN(29, 17, 0); // D29 is P0.17 -- PIN_QSPI_DATA0
//_FL_DEFPIN(30, 22, 0); // D30 is P0.22 -- PIN_QSPI_DATA1
//_FL_DEFPIN(31, 23, 0); // D31 is P0.23 -- PIN_QSPI_DATA2
// Arduino pins 32..34
//_FL_DEFPIN(32, 21, 0); // D32 is P0.21 -- PIN_QSPI_DATA3
//_FL_DEFPIN(33, 9, 0); // D33 is NFC1, only accessible via test point
#endif // defined (ARDUINO_NRF52840_FEATHER)
// Adafruit Bluefruit nRF52840 Metro Express
// From https://www.adafruit.com/package_adafruit_index.json
#if defined (ARDUINO_NRF52840_METRO)
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
#warning "Adafruit Bluefruit nRF52840 Metro Express is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
#endif
_FL_DEFPIN( 0, 25, 0); // D0 is P0.25 (UART TX)
_FL_DEFPIN( 1, 24, 0); // D1 is P0.24 (UART RX)
_FL_DEFPIN( 2, 10, 1); // D2 is P1.10
_FL_DEFPIN( 3, 4, 1); // D3 is P1.04
_FL_DEFPIN( 4, 11, 1); // D4 is P1.11
_FL_DEFPIN( 5, 12, 1); // D5 is P1.12
_FL_DEFPIN( 6, 14, 1); // D6 is P1.14
_FL_DEFPIN( 7, 26, 0); // D7 is P0.26
_FL_DEFPIN( 8, 27, 0); // D8 is P0.27
_FL_DEFPIN( 9, 12, 0); // D9 is P0.12
_FL_DEFPIN(10, 6, 0); // D10 is P0.06
_FL_DEFPIN(11, 8, 0); // D11 is P0.08
_FL_DEFPIN(12, 9, 1); // D12 is P1.09
_FL_DEFPIN(13, 14, 0); // D13 is P0.14
_FL_DEFPIN(14, 4, 0); // D14 is P0.04 (A0)
_FL_DEFPIN(15, 5, 0); // D15 is P0.05 (A1)
_FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2)
_FL_DEFPIN(17, 30, 0); // D17 is P0.30 (A3)
_FL_DEFPIN(18, 2, 0); // D18 is P0.02 (A4)
_FL_DEFPIN(19, 3, 0); // D19 is P0.03 (A5)
_FL_DEFPIN(20, 29, 0); // D20 is P0.29 (A6, battery)
_FL_DEFPIN(21, 31, 0); // D21 is P0.31 (A7, ARef)
_FL_DEFPIN(22, 15, 0); // D22 is P0.15 (SDA)
_FL_DEFPIN(23, 16, 0); // D23 is P0.16 (SCL)
_FL_DEFPIN(24, 11, 0); // D24 is P0.11 (SPI MISO)
_FL_DEFPIN(25, 8, 1); // D25 is P1.08 (SPI MOSI)
_FL_DEFPIN(26, 7, 0); // D26 is P0.07 (SPI SCK )
//_FL_DEFPIN(27, 19, 0); // D27 is P0.19 (QSPI CLK )
//_FL_DEFPIN(28, 20, 0); // D28 is P0.20 (QSPI CS )
//_FL_DEFPIN(29, 17, 0); // D29 is P0.17 (QSPI Data 0)
//_FL_DEFPIN(30, 23, 0); // D30 is P0.23 (QSPI Data 1)
//_FL_DEFPIN(31, 22, 0); // D31 is P0.22 (QSPI Data 2)
//_FL_DEFPIN(32, 21, 0); // D32 is P0.21 (QSPI Data 3)
_FL_DEFPIN(33, 13, 1); // D33 is P1.13 LED1
_FL_DEFPIN(34, 15, 1); // D34 is P1.15 LED2
_FL_DEFPIN(35, 13, 0); // D35 is P0.13 NeoPixel
_FL_DEFPIN(36, 0, 1); // D36 is P1.02 Switch
_FL_DEFPIN(37, 0, 1); // D37 is P1.00 SWO/DFU
_FL_DEFPIN(38, 9, 0); // D38 is P0.09 NFC1
_FL_DEFPIN(39, 10, 0); // D39 is P0.10 NFC2
#endif // defined (ARDUINO_NRF52840_METRO)
// Adafruit Bluefruit on nRF52840DK PCA10056
// From https://www.adafruit.com/package_adafruit_index.json
#if defined (ARDUINO_NRF52840_PCA10056)
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
#if defined(USE_ARDUINO_PIN_NUMBERING)
#error "Define of `USE_ARDUINO_PIN_NUMBERING` has known errors in pin mapping -- select different mapping"
#elif defined(FASTLED_NRF52_USE_ARDUINO_UNO_R3_HEADER_PIN_NUMBERING)
/* The following allows defining and using the FastPin<> templates,
using the Arduino UNO R3 connector pin definitions.
*/
_FL_DEFPIN( 0, 1, 1); // D0 is P1.01
_FL_DEFPIN( 1, 2, 1); // D1 is P1.02
_FL_DEFPIN( 2, 3, 1); // D2 is P1.03
_FL_DEFPIN( 3, 4, 1); // D3 is P1.04
_FL_DEFPIN( 4, 5, 1); // D4 is P1.05
_FL_DEFPIN( 5, 6, 1); // D5 is P1.06
_FL_DEFPIN( 6, 7, 1); // D6 is P1.07 (BUTTON1 option)
_FL_DEFPIN( 7, 8, 1); // D7 is P1.08 (BUTTON2 option)
_FL_DEFPIN( 8, 10, 1); // D8 is P1.10
_FL_DEFPIN( 9, 11, 1); // D9 is P1.11
_FL_DEFPIN(10, 12, 1); // D10 is P1.12
_FL_DEFPIN(11, 13, 1); // D11 is P1.13
_FL_DEFPIN(12, 14, 1); // D12 is P1.14
_FL_DEFPIN(13, 15, 1); // D13 is P1.15
// Arduino UNO uses pins D14..D19 to map to header pins A0..A5
// AREF has no equivalent digital pin map on Arduino, would be P0.02
_FL_DEFPIN(14, 3, 0); // D14 / A0 is P0.03
_FL_DEFPIN(15, 4, 0); // D15 / A1 is P0.04
_FL_DEFPIN(16, 28, 0); // D16 / A2 is P0.28
_FL_DEFPIN(17, 29, 0); // D17 / A3 is P0.29
// Cannot determine which pin on PCA10056 would be intended solely from UNO R3 digital pin number
//_FL_DEFPIN(18, 30, 0); // D18 could be one of two pins: A4 would be P0.30, SDA would be P0.26
//_FL_DEFPIN(19, 31, 0); // D19 could be one of two pins: A5 would be P0.31, SCL would be P0.27
#elif defined(FASTLED_NRF52_USE_ARDUINO_MEGA_2560_REV3_HEADER_PIN_NUMBERING)
/* The following allows defining and using the FastPin<> templates,
using the Arduino UNO R3 connector pin definitions.
*/
_FL_DEFPIN( 0, 1, 1); // D0 is P1.01
_FL_DEFPIN( 1, 2, 1); // D1 is P1.02
_FL_DEFPIN( 2, 3, 1); // D2 is P1.03
_FL_DEFPIN( 3, 4, 1); // D3 is P1.04
_FL_DEFPIN( 4, 5, 1); // D4 is P1.05
_FL_DEFPIN( 5, 6, 1); // D5 is P1.06
_FL_DEFPIN( 6, 7, 1); // D6 is P1.07 (BUTTON1 option)
_FL_DEFPIN( 7, 8, 1); // D7 is P1.08 (BUTTON2 option)
_FL_DEFPIN( 8, 10, 1); // D8 is P1.10
_FL_DEFPIN( 9, 11, 1); // D9 is P1.11
_FL_DEFPIN(10, 12, 1); // D10 is P1.12
_FL_DEFPIN(11, 13, 1); // D11 is P1.13
_FL_DEFPIN(12, 14, 1); // D12 is P1.14
_FL_DEFPIN(13, 15, 1); // D13 is P1.15
// Arduino MEGA 2560 has additional digital pins on lower digital header
_FL_DEFPIN(14, 10, 0); // D14 is P0.10
_FL_DEFPIN(15, 9, 0); // D15 is P0.09
_FL_DEFPIN(16, 8, 0); // D16 is P0.08
_FL_DEFPIN(17, 7, 0); // D17 is P0.07
_FL_DEFPIN(18, 6, 0); // D14 is P0.06
_FL_DEFPIN(19, 5, 0); // D15 is P0.05
// Cannot determine which pin on PCA10056 would be intended solely from UNO MEGA 2560 digital pin number
//_FL_DEFPIN(20, 1, 0); // D20 could be one of two pins: D20 on lower header would be P0.01, SDA would be P0.26
//_FL_DEFPIN(21, 0, 0); // D21 could be one of two pins: D21 on lower header would be P0.00, SCL would be P0.27
// Arduino MEGA 2560 has D22-D53 exposed on perpendicular two-row header
// PCA10056 has support for D22-D38 via a 2x19 header at that location (D39 is GND on PCA10056)
_FL_DEFPIN(22, 11, 0); // D22 is P0.11
_FL_DEFPIN(23, 12, 0); // D23 is P0.12
_FL_DEFPIN(24, 13, 0); // D24 is P0.13
_FL_DEFPIN(25, 14, 0); // D25 is P0.14
_FL_DEFPIN(26, 15, 0); // D26 is P0.15
_FL_DEFPIN(27, 16, 0); // D27 is P0.16
// _FL_DEFPIN(28, 17, 0); // D28 is P0.17 (QSPI !CS )
// _FL_DEFPIN(29, 18, 0); // D29 is P0.18 (RESET)
// _FL_DEFPIN(30, 19, 0); // D30 is P0.19 (QSPI CLK)
// _FL_DEFPIN(31, 20, 0); // D31 is P0.20 (QSPI DIO0)
// _FL_DEFPIN(32, 21, 0); // D32 is P0.21 (QSPI DIO1)
// _FL_DEFPIN(33, 22, 0); // D33 is P0.22 (QSPI DIO2)
// _FL_DEFPIN(34, 23, 0); // D34 is P0.23 (QSPI DIO3)
_FL_DEFPIN(35, 24, 0); // D35 is P0.24
_FL_DEFPIN(36, 25, 0); // D36 is P0.25
_FL_DEFPIN(37, 0, 1); // D37 is P1.00
_FL_DEFPIN(38, 9, 1); // D38 is P1.09
// _FL_DEFPIN(39, , 0); // D39 is P0.
// Arduino MEGA 2560 uses pins D54..D59 to map to header pins A0..A5
// (it also has D60..D69 for A6..A15, which have no corresponding header on PCA10056)
// AREF has no equivalent digital pin map on Arduino, would be P0.02
_FL_DEFPIN(54, 3, 0); // D54 / A0 is P0.03
_FL_DEFPIN(55, 4, 0); // D55 / A1 is P0.04
_FL_DEFPIN(56, 28, 0); // D56 / A2 is P0.28
_FL_DEFPIN(57, 29, 0); // D57 / A3 is P0.29
_FL_DEFPIN(58, 30, 0); // D58 / A4 is P0.30
_FL_DEFPIN(59, 31, 0); // D59 / A5 is P0.31
#else // identity mapping of arduino pin to port/pin
/* 48 pins, defined using natural mapping in Adafruit's variant.cpp (!) */
_DEFPIN_ARM_IDENTITY_P0( 0); // P0.00 (XL1 .. ensure SB4 bridged, SB2 cut)
_DEFPIN_ARM_IDENTITY_P0( 1); // P0.01 (XL2 .. ensure SB3 bridged, SB1 cut)
_DEFPIN_ARM_IDENTITY_P0( 2); // P0.02 (AIN0)
_DEFPIN_ARM_IDENTITY_P0( 3); // P0.03 (AIN1)
_DEFPIN_ARM_IDENTITY_P0( 4); // P0.04 (AIN2 / UART CTS option)
_DEFPIN_ARM_IDENTITY_P0( 5); // P0.05 (AIN3 / UART RTS)
_DEFPIN_ARM_IDENTITY_P0( 6); // P0.06 (UART TxD)
_DEFPIN_ARM_IDENTITY_P0( 7); // P0.07 (TRACECLK / UART CTS default)
_DEFPIN_ARM_IDENTITY_P0( 8); // P0.08 (UART RxD)
_DEFPIN_ARM_IDENTITY_P0( 9); // P0.09 (NFC1)
_DEFPIN_ARM_IDENTITY_P0(10); // P0.10 (NFC2)
_DEFPIN_ARM_IDENTITY_P0(11); // P0.11 (TRACEDATA2 / BUTTON1 default)
_DEFPIN_ARM_IDENTITY_P0(12); // P0.12 (TRACEDATA1 / BUTTON2 default)
_DEFPIN_ARM_IDENTITY_P0(13); // P0.13 (LED1)
_DEFPIN_ARM_IDENTITY_P0(14); // P0.14 (LED2)
_DEFPIN_ARM_IDENTITY_P0(15); // P0.15 (LED3)
_DEFPIN_ARM_IDENTITY_P0(16); // P0.16 (LED4)
//_DEFPIN_ARM_IDENTITY_P0(17); // P0.17 (QSPI !CS )
//_DEFPIN_ARM_IDENTITY_P0(18); // P0.18 (RESET)
//_DEFPIN_ARM_IDENTITY_P0(19); // P0.19 (QSPI CLK )
//_DEFPIN_ARM_IDENTITY_P0(20); // P0.20 (QSPI DIO0)
//_DEFPIN_ARM_IDENTITY_P0(21); // P0.21 (QSPI DIO1)
//_DEFPIN_ARM_IDENTITY_P0(22); // P0.22 (QSPI DIO2)
//_DEFPIN_ARM_IDENTITY_P0(23); // P0.23 (QSPI DIO3)
_DEFPIN_ARM_IDENTITY_P0(24); // P0.24 (BUTTON3)
_DEFPIN_ARM_IDENTITY_P0(25); // P0.25 (BUTTON4)
_DEFPIN_ARM_IDENTITY_P0(26); // P0.26
_DEFPIN_ARM_IDENTITY_P0(27); // P0.27
_DEFPIN_ARM_IDENTITY_P0(28); // P0.28 (AIN4)
_DEFPIN_ARM_IDENTITY_P0(29); // P0.29 (AIN5)
_DEFPIN_ARM_IDENTITY_P0(30); // P0.30 (AIN6)
_DEFPIN_ARM_IDENTITY_P0(31); // P0.31 (AIN7)
_DEFPIN_ARM_IDENTITY_P0(32); // P1.00 (SWO / TRACEDATA0)
_DEFPIN_ARM_IDENTITY_P0(33); // P1.01
_DEFPIN_ARM_IDENTITY_P0(34); // P1.02
_DEFPIN_ARM_IDENTITY_P0(35); // P1.03
_DEFPIN_ARM_IDENTITY_P0(36); // P1.04
_DEFPIN_ARM_IDENTITY_P0(37); // P1.05
_DEFPIN_ARM_IDENTITY_P0(38); // P1.06
_DEFPIN_ARM_IDENTITY_P0(39); // P1.07 (BUTTON1 option)
_DEFPIN_ARM_IDENTITY_P0(40); // P1.08 (BUTTON2 option)
_DEFPIN_ARM_IDENTITY_P0(41); // P1.09 (TRACEDATA3)
_DEFPIN_ARM_IDENTITY_P0(42); // P1.10
_DEFPIN_ARM_IDENTITY_P0(43); // P1.11
_DEFPIN_ARM_IDENTITY_P0(44); // P1.12
_DEFPIN_ARM_IDENTITY_P0(45); // P1.13
_DEFPIN_ARM_IDENTITY_P0(46); // P1.14
_DEFPIN_ARM_IDENTITY_P0(47); // P1.15
#endif
#endif // defined (ARDUINO_NRF52840_PCA10056)
// Adafruit ItsyBitsy nRF52840 Express
// From https://www.adafruit.com/package_adafruit_index.json
#if defined (ARDUINO_NRF52_ITSYBITSY)
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
#warning "Adafruit ItsyBitsy nRF52840 Express is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
#endif
// [D0 .. D13] (digital)
_FL_DEFPIN( 0, 25, 0); // D0 is P0.25 (UART RX)
_FL_DEFPIN( 1, 24, 0); // D1 is P0.24 (UART TX)
_FL_DEFPIN( 2, 2, 1); // D2 is P1.02
_FL_DEFPIN( 3, 6, 0); // D3 is P0.06 LED
_FL_DEFPIN( 4, 29, 0); // D4 is P0.29 Button
_FL_DEFPIN( 5, 27, 0); // D5 is P0.27
_FL_DEFPIN( 6, 9, 1); // D6 is P1.09 (DotStar Clock)
_FL_DEFPIN( 7, 8, 1); // D7 is P1.08
_FL_DEFPIN( 8, 8, 0); // D8 is P0.08 (DotStar Data)
_FL_DEFPIN( 9, 7, 0); // D9 is P0.07
_FL_DEFPIN(10, 5, 0); // D10 is P0.05
_FL_DEFPIN(11, 26, 0); // D11 is P0.26
_FL_DEFPIN(12, 11, 0); // D12 is P0.11
_FL_DEFPIN(13, 12, 0); // D13 is P0.12
// [D14 .. D20] (analog [A0 .. A6])
_FL_DEFPIN(14, 4, 0); // D14 is P0.04 (A0)
_FL_DEFPIN(15, 30, 0); // D15 is P0.30 (A1)
_FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2)
_FL_DEFPIN(17, 31, 0); // D17 is P0.31 (A3)
_FL_DEFPIN(18, 2, 0); // D18 is P0.02 (A4)
_FL_DEFPIN(19, 3, 0); // D19 is P0.03 (A5)
_FL_DEFPIN(20, 5, 0); // D20 is P0.05 (A6/D10)
// [D21 .. D22] (I2C)
_FL_DEFPIN(21, 16, 0); // D21 is P0.16 (SDA)
_FL_DEFPIN(22, 14, 0); // D22 is P0.14 (SCL)
// [D23 .. D25] (SPI)
_FL_DEFPIN(23, 20, 0); // D23 is P0.20 (SPI MISO)
_FL_DEFPIN(24, 15, 0); // D24 is P0.15 (SPI MOSI)
_FL_DEFPIN(25, 13, 0); // D25 is P0.13 (SPI SCK )
// [D26 .. D31] (QSPI)
_FL_DEFPIN(26, 19, 0); // D26 is P0.19 (QSPI CLK)
_FL_DEFPIN(27, 23, 0); // D27 is P0.23 (QSPI CS)
_FL_DEFPIN(28, 21, 0); // D28 is P0.21 (QSPI Data 0)
_FL_DEFPIN(29, 22, 0); // D29 is P0.22 (QSPI Data 1)
_FL_DEFPIN(30, 0, 1); // D30 is P1.00 (QSPI Data 2)
_FL_DEFPIN(31, 17, 0); // D31 is P0.17 (QSPI Data 3)
#endif // defined (ARDUINO_NRF52_ITSYBITSY)
// Electronut labs bluey
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/bluey/variant.cpp
#if defined(ARDUINO_ELECTRONUT_BLUEY)
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
#warning "Electronut labs bluey is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
#endif
_FL_DEFPIN( 0, 26, 0); // D0 is P0.26
_FL_DEFPIN( 1, 27, 0); // D1 is P0.27
_FL_DEFPIN( 2, 22, 0); // D2 is P0.22 (SPI SS )
_FL_DEFPIN( 3, 23, 0); // D3 is P0.23 (SPI MOSI)
_FL_DEFPIN( 4, 24, 0); // D4 is P0.24 (SPI MISO, also A3)
_FL_DEFPIN( 5, 25, 0); // D5 is P0.25 (SPI SCK )
_FL_DEFPIN( 6, 16, 0); // D6 is P0.16 (Button)
_FL_DEFPIN( 7, 19, 0); // D7 is P0.19 (R)
_FL_DEFPIN( 8, 18, 0); // D8 is P0.18 (G)
_FL_DEFPIN( 9, 17, 0); // D9 is P0.17 (B)
_FL_DEFPIN(10, 11, 0); // D10 is P0.11 (SCL)
_FL_DEFPIN(11, 12, 0); // D11 is P0.12 (DRDYn)
_FL_DEFPIN(12, 13, 0); // D12 is P0.13 (SDA)
_FL_DEFPIN(13, 14, 0); // D13 is P0.17 (INT)
_FL_DEFPIN(14, 15, 0); // D14 is P0.15 (INT1)
_FL_DEFPIN(15, 20, 0); // D15 is P0.20 (INT2)
_FL_DEFPIN(16, 2, 0); // D16 is P0.02 (A0)
_FL_DEFPIN(17, 3, 0); // D17 is P0.03 (A1)
_FL_DEFPIN(18, 4, 0); // D18 is P0.04 (A2)
_FL_DEFPIN(19, 24, 0); // D19 is P0.24 (A3, also D4/SPI MISO) -- is this right?
_FL_DEFPIN(20, 29, 0); // D20 is P0.29 (A4)
_FL_DEFPIN(21, 30, 0); // D21 is P0.30 (A5)
_FL_DEFPIN(22, 31, 0); // D22 is P0.31 (A6)
_FL_DEFPIN(23, 8, 0); // D23 is P0.08 (RX)
_FL_DEFPIN(24, 6, 0); // D24 is P0.06 (TX)
_FL_DEFPIN(25, 5, 0); // D25 is P0.05 (RTS)
_FL_DEFPIN(26, 7, 0); // D26 is P0.07 (CTS)
#endif // defined(ARDUINO_ELECTRONUT_BLUEY)
// Electronut labs hackaBLE
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/hackaBLE/variant.cpp
#if defined(ARDUINO_ELECTRONUT_HACKABLE)
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
#warning "Electronut labs hackaBLE is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
#endif
_FL_DEFPIN( 0, 14, 0); // D0 is P0.14 (RX)
_FL_DEFPIN( 1, 13, 0); // D1 is P0.13 (TX)
_FL_DEFPIN( 2, 12, 0); // D2 is P0.12
_FL_DEFPIN( 3, 11, 0); // D3 is P0.11 (SPI MOSI)
_FL_DEFPIN( 4, 8, 0); // D4 is P0.08 (SPI MISO)
_FL_DEFPIN( 5, 7, 0); // D5 is P0.07 (SPI SCK )
_FL_DEFPIN( 6, 6, 0); // D6 is P0.06
_FL_DEFPIN( 7, 27, 0); // D7 is P0.27
_FL_DEFPIN( 8, 26, 0); // D8 is P0.26
_FL_DEFPIN( 9, 25, 0); // D9 is P0.25
_FL_DEFPIN(10, 5, 0); // D10 is P0.05 (A3)
_FL_DEFPIN(11, 4, 0); // D11 is P0.04 (A2)
_FL_DEFPIN(12, 3, 0); // D12 is P0.03 (A1)
_FL_DEFPIN(13, 2, 0); // D13 is P0.02 (A0 / AREF)
_FL_DEFPIN(14, 23, 0); // D14 is P0.23
_FL_DEFPIN(15, 22, 0); // D15 is P0.22
_FL_DEFPIN(16, 18, 0); // D16 is P0.18
_FL_DEFPIN(17, 16, 0); // D17 is P0.16
_FL_DEFPIN(18, 15, 0); // D18 is P0.15
_FL_DEFPIN(19, 24, 0); // D19 is P0.24
_FL_DEFPIN(20, 28, 0); // D20 is P0.28 (A4)
_FL_DEFPIN(21, 29, 0); // D21 is P0.29 (A5)
_FL_DEFPIN(22, 30, 0); // D22 is P0.30 (A6)
_FL_DEFPIN(23, 31, 0); // D23 is P0.31 (A7)
_FL_DEFPIN(24, 19, 0); // D24 is P0.19 (RED LED)
_FL_DEFPIN(25, 20, 0); // D25 is P0.20 (GREEN LED)
_FL_DEFPIN(26, 17, 0); // D26 is P0.17 (BLUE LED)
#endif // defined(ARDUINO_ELECTRONUT_HACKABLE)
// Electronut labs hackaBLE_v2
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/hackaBLE_v2/variant.cpp
// (32 pins, natural mapping)
#if defined(ARDUINO_ELECTRONUT_hackaBLE_v2)
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
#warning "Electronut labs hackaBLE_v2 is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
#endif
_DEFPIN_ARM_IDENTITY_P0( 0); // P0.00
_DEFPIN_ARM_IDENTITY_P0( 1); // P0.01
_DEFPIN_ARM_IDENTITY_P0( 2); // P0.02 (A0 / SDA / AREF)
_DEFPIN_ARM_IDENTITY_P0( 3); // P0.03 (A1 / SCL )
_DEFPIN_ARM_IDENTITY_P0( 4); // P0.04 (A2)
_DEFPIN_ARM_IDENTITY_P0( 5); // P0.05 (A3)
_DEFPIN_ARM_IDENTITY_P0( 6); // P0.06
_DEFPIN_ARM_IDENTITY_P0( 7); // P0.07 (RX)
_DEFPIN_ARM_IDENTITY_P0( 8); // P0.08 (TX)
_DEFPIN_ARM_IDENTITY_P0( 9); // P0.09
_DEFPIN_ARM_IDENTITY_P0(10); // P0.10
_DEFPIN_ARM_IDENTITY_P0(11); // P0.11 (SPI MISO)
_DEFPIN_ARM_IDENTITY_P0(12); // P0.12 (SPI MOSI)
_DEFPIN_ARM_IDENTITY_P0(13); // P0.13 (SPI SCK )
_DEFPIN_ARM_IDENTITY_P0(14); // P0.14 (SPI SS )
_DEFPIN_ARM_IDENTITY_P0(15); // P0.15
_DEFPIN_ARM_IDENTITY_P0(16); // P0.16
_DEFPIN_ARM_IDENTITY_P0(17); // P0.17 (BLUE LED)
_DEFPIN_ARM_IDENTITY_P0(18); // P0.18
_DEFPIN_ARM_IDENTITY_P0(19); // P0.19 (RED LED)
_DEFPIN_ARM_IDENTITY_P0(20); // P0.20 (GREEN LED)
// _DEFPIN_ARM_IDENTITY_P0(21); // P0.21 (RESET)
_DEFPIN_ARM_IDENTITY_P0(22); // P0.22
_DEFPIN_ARM_IDENTITY_P0(23); // P0.23
_DEFPIN_ARM_IDENTITY_P0(24); // P0.24
_DEFPIN_ARM_IDENTITY_P0(25); // P0.25
_DEFPIN_ARM_IDENTITY_P0(26); // P0.26
_DEFPIN_ARM_IDENTITY_P0(27); // P0.27
_DEFPIN_ARM_IDENTITY_P0(28); // P0.28 (A4)
_DEFPIN_ARM_IDENTITY_P0(29); // P0.29 (A5)
_DEFPIN_ARM_IDENTITY_P0(30); // P0.30 (A6)
_DEFPIN_ARM_IDENTITY_P0(31); // P0.31 (A7)
#endif // defined(ARDUINO_ELECTRONUT_hackaBLE_v2)
// RedBear Blend 2
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/RedBear_Blend2/variant.cpp
#if defined(ARDUINO_RB_BLEND_2)
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
#warning "RedBear Blend 2 is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
#endif
_FL_DEFPIN( 0, 11, 0); // D0 is P0.11
_FL_DEFPIN( 1, 12, 0); // D1 is P0.12
_FL_DEFPIN( 2, 13, 0); // D2 is P0.13
_FL_DEFPIN( 3, 14, 0); // D3 is P0.14
_FL_DEFPIN( 4, 15, 0); // D4 is P0.15
_FL_DEFPIN( 5, 16, 0); // D5 is P0.16
_FL_DEFPIN( 6, 17, 0); // D6 is P0.17
_FL_DEFPIN( 7, 18, 0); // D7 is P0.18
_FL_DEFPIN( 8, 19, 0); // D8 is P0.19
_FL_DEFPIN( 9, 20, 0); // D9 is P0.20
_FL_DEFPIN(10, 22, 0); // D10 is P0.22 (SPI SS )
_FL_DEFPIN(11, 23, 0); // D11 is P0.23 (SPI MOSI)
_FL_DEFPIN(12, 24, 0); // D12 is P0.24 (SPI MISO)
_FL_DEFPIN(13, 25, 0); // D13 is P0.25 (SPI SCK / LED)
_FL_DEFPIN(14, 3, 0); // D14 is P0.03 (A0)
_FL_DEFPIN(15, 4, 0); // D15 is P0.04 (A1)
_FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2)
_FL_DEFPIN(17, 29, 0); // D17 is P0.29 (A3)
_FL_DEFPIN(18, 30, 0); // D18 is P0.30 (A4)
_FL_DEFPIN(19, 31, 0); // D19 is P0.31 (A5)
_FL_DEFPIN(20, 26, 0); // D20 is P0.26 (SDA)
_FL_DEFPIN(21, 27, 0); // D21 is P0.27 (SCL)
_FL_DEFPIN(22, 8, 0); // D22 is P0.08 (RX)
_FL_DEFPIN(23, 6, 0); // D23 is P0.06 (TX)
_FL_DEFPIN(24, 2, 0); // D24 is P0.02 (AREF)
#endif // defined(ARDUINO_RB_BLEND_2)
// RedBear BLE Nano 2
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/RedBear_BLENano2/variant.cpp
#if defined(ARDUINO_RB_BLE_NANO_2)
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
#warning "RedBear BLE Nano 2 is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
#endif
_FL_DEFPIN( 0, 30, 0); // D0 is P0.30 (A0 / RX)
_FL_DEFPIN( 1, 29, 0); // D1 is P0.29 (A1 / TX)
_FL_DEFPIN( 2, 28, 0); // D2 is P0.28 (A2 / SDA)
_FL_DEFPIN( 3, 2, 0); // D3 is P0.02 (A3 / SCL)
_FL_DEFPIN( 4, 5, 0); // D4 is P0.05 (A4)
_FL_DEFPIN( 5, 4, 0); // D5 is P0.04 (A5)
_FL_DEFPIN( 6, 3, 0); // D6 is P0.03 (SPI SS )
_FL_DEFPIN( 7, 6, 0); // D7 is P0.06 (SPI MOSI)
_FL_DEFPIN( 8, 7, 0); // D8 is P0.07 (SPI MISO)
_FL_DEFPIN( 9, 8, 0); // D9 is P0.08 (SPI SCK )
// _FL_DEFPIN(10, 21, 0); // D10 is P0.21 (RESET)
_FL_DEFPIN(13, 11, 0); // D11 is P0.11 (LED)
#endif // defined(ARDUINO_RB_BLE_NANO_2)
// Nordic Semiconductor nRF52 DK
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/nRF52DK/variant.cpp
#if defined(ARDUINO_NRF52_DK)
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
#warning "Nordic Semiconductor nRF52 DK is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
#endif
_FL_DEFPIN( 0, 11, 0); // D0 is P0.11
_FL_DEFPIN( 1, 12, 0); // D1 is P0.12
_FL_DEFPIN( 2, 13, 0); // D2 is P0.13 (BUTTON1)
_FL_DEFPIN( 3, 14, 0); // D3 is P0.14 (BUTTON2)
_FL_DEFPIN( 4, 15, 0); // D4 is P0.15 (BUTTON3)
_FL_DEFPIN( 5, 16, 0); // D5 is P0.16 (BUTTON4)
_FL_DEFPIN( 6, 17, 0); // D6 is P0.17 (LED1)
_FL_DEFPIN( 7, 18, 0); // D7 is P0.18 (LED2)
_FL_DEFPIN( 8, 19, 0); // D8 is P0.19 (LED3)
_FL_DEFPIN( 9, 20, 0); // D9 is P0.20 (LED4)
_FL_DEFPIN(10, 22, 0); // D10 is P0.22 (SPI SS )
_FL_DEFPIN(11, 23, 0); // D11 is P0.23 (SPI MOSI)
_FL_DEFPIN(12, 24, 0); // D12 is P0.24 (SPI MISO)
_FL_DEFPIN(13, 25, 0); // D13 is P0.25 (SPI SCK / LED)
_FL_DEFPIN(14, 3, 0); // D14 is P0.03 (A0)
_FL_DEFPIN(15, 4, 0); // D15 is P0.04 (A1)
_FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2)
_FL_DEFPIN(17, 29, 0); // D17 is P0.29 (A3)
_FL_DEFPIN(18, 30, 0); // D18 is P0.30 (A4)
_FL_DEFPIN(19, 31, 0); // D19 is P0.31 (A5)
_FL_DEFPIN(20, 5, 0); // D20 is P0.05 (A6)
_FL_DEFPIN(21, 2, 0); // D21 is P0.02 (A7 / AREF)
_FL_DEFPIN(22, 26, 0); // D22 is P0.26 (SDA)
_FL_DEFPIN(23, 27, 0); // D23 is P0.27 (SCL)
_FL_DEFPIN(24, 8, 0); // D24 is P0.08 (RX)
_FL_DEFPIN(25, 6, 0); // D25 is P0.06 (TX)
#endif // defined(ARDUINO_NRF52_DK)
// Taida Century nRF52 mini board
// https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/Taida_Century_nRF52_minidev/variant.cpp
#if defined(ARDUINO_STCT_NRF52_minidev)
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
#warning "Taida Century nRF52 mini board is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
#endif
//_FL_DEFPIN( 0, 25, 0); // D0 is P0.xx (near radio!)
//_FL_DEFPIN( 1, 26, 0); // D1 is P0.xx (near radio!)
//_FL_DEFPIN( 2, 27, 0); // D2 is P0.xx (near radio!)
//_FL_DEFPIN( 3, 28, 0); // D3 is P0.xx (near radio!)
//_FL_DEFPIN( 4, 29, 0); // D4 is P0.xx (Not connected, near radio!)
//_FL_DEFPIN( 5, 30, 0); // D5 is P0.xx (LED1, near radio!)
//_FL_DEFPIN( 6, 31, 0); // D6 is P0.xx (LED2, near radio!)
_FL_DEFPIN( 7, 2, 0); // D7 is P0.xx (SDA)
_FL_DEFPIN( 8, 3, 0); // D8 is P0.xx (SCL)
_FL_DEFPIN( 9, 4, 0); // D9 is P0.xx (BUTTON1 / NFC1)
_FL_DEFPIN(10, 5, 0); // D10 is P0.xx
//_FL_DEFPIN(11, 0, 0); // D11 is P0.xx (Not connected)
//_FL_DEFPIN(12, 1, 0); // D12 is P0.xx (Not connected)
_FL_DEFPIN(13, 6, 0); // D13 is P0.xx
_FL_DEFPIN(14, 7, 0); // D14 is P0.xx
_FL_DEFPIN(15, 8, 0); // D15 is P0.xx
//_FL_DEFPIN(16, 9, 0); // D16 is P0.xx (Not connected)
//_FL_DEFPIN(17, 10, 0); // D17 is P0.xx (NFC2, Not connected)
_FL_DEFPIN(18, 11, 0); // D18 is P0.xx (RXD)
_FL_DEFPIN(19, 12, 0); // D19 is P0.xx (TXD)
_FL_DEFPIN(20, 13, 0); // D20 is P0.xx (SPI SS )
_FL_DEFPIN(21, 14, 0); // D21 is P0.xx (SPI MISO)
_FL_DEFPIN(22, 15, 0); // D22 is P0.xx (SPI MOSI)
_FL_DEFPIN(23, 16, 0); // D23 is P0.xx (SPI SCK )
_FL_DEFPIN(24, 17, 0); // D24 is P0.xx (A0)
_FL_DEFPIN(25, 18, 0); // D25 is P0.xx (A1)
_FL_DEFPIN(26, 19, 0); // D26 is P0.xx (A2)
_FL_DEFPIN(27, 20, 0); // D27 is P0.xx (A3)
//_FL_DEFPIN(28, 22, 0); // D28 is P0.xx (A4, near radio!)
//_FL_DEFPIN(29, 23, 0); // D29 is P0.xx (A5, near radio!)
_FL_DEFPIN(30, 24, 0); // D30 is P0.xx
// _FL_DEFPIN(31, 21, 0); // D31 is P0.21 (RESET)
#endif // defined(ARDUINO_STCT_NRF52_minidev)
// Generic nRF52832
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/boards.txt
#if defined(ARDUINO_GENERIC) && ( defined(NRF52832_XXAA) || defined(NRF52832_XXAB) )
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
#error "Cannot define more than one board at a time"
#else
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
#endif
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
#warning "Using `generic` NRF52832 board is an untested configuration -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
#endif
_DEFPIN_ARM_IDENTITY_P0( 0); // P0.00 ( UART RX
_DEFPIN_ARM_IDENTITY_P0( 1); // P0.01 (A0, UART TX)
_DEFPIN_ARM_IDENTITY_P0( 2); // P0.02 (A1)
_DEFPIN_ARM_IDENTITY_P0( 3); // P0.03 (A2)
_DEFPIN_ARM_IDENTITY_P0( 4); // P0.04 (A3)
_DEFPIN_ARM_IDENTITY_P0( 5); // P0.05 (A4)
_DEFPIN_ARM_IDENTITY_P0( 6); // P0.06 (A5)
_DEFPIN_ARM_IDENTITY_P0( 7); // P0.07
_DEFPIN_ARM_IDENTITY_P0( 8); // P0.08
_DEFPIN_ARM_IDENTITY_P0( 9); // P0.09
_DEFPIN_ARM_IDENTITY_P0(10); // P0.10
_DEFPIN_ARM_IDENTITY_P0(11); // P0.11
_DEFPIN_ARM_IDENTITY_P0(12); // P0.12
_DEFPIN_ARM_IDENTITY_P0(13); // P0.13 (LED)
_DEFPIN_ARM_IDENTITY_P0(14); // P0.14
_DEFPIN_ARM_IDENTITY_P0(15); // P0.15
_DEFPIN_ARM_IDENTITY_P0(16); // P0.16
_DEFPIN_ARM_IDENTITY_P0(17); // P0.17
_DEFPIN_ARM_IDENTITY_P0(18); // P0.18
_DEFPIN_ARM_IDENTITY_P0(19); // P0.19
_DEFPIN_ARM_IDENTITY_P0(20); // P0.20 (I2C SDA)
_DEFPIN_ARM_IDENTITY_P0(21); // P0.21 (I2C SCL)
_DEFPIN_ARM_IDENTITY_P0(22); // P0.22 (SPI MISO)
_DEFPIN_ARM_IDENTITY_P0(23); // P0.23 (SPI MOSI)
_DEFPIN_ARM_IDENTITY_P0(24); // P0.24 (SPI SCK )
_DEFPIN_ARM_IDENTITY_P0(25); // P0.25 (SPI SS )
_DEFPIN_ARM_IDENTITY_P0(26); // P0.26
_DEFPIN_ARM_IDENTITY_P0(27); // P0.27
_DEFPIN_ARM_IDENTITY_P0(28); // P0.28
_DEFPIN_ARM_IDENTITY_P0(29); // P0.29
_DEFPIN_ARM_IDENTITY_P0(30); // P0.30
_DEFPIN_ARM_IDENTITY_P0(31); // P0.31
#endif // defined(ARDUINO_GENERIC)
#endif // __FASTPIN_ARM_NRF52_VARIANTS_H

View File

@@ -0,0 +1,340 @@
#ifndef __FASTSPI_ARM_NRF52_H
#define __FASTSPI_ARM_NRF52_H
#ifndef FASTLED_FORCE_SOFTWARE_SPI
#include <nrf_spim.h>
#define FASTLED_ALL_PINS_HARDWARE_SPI
// NRF52810 has SPIM0: Frequencies from 125kbps to 8Mbps
// NRF52832 adds SPIM1, SPIM2 (same frequencies)
// NRF52840 adds SPIM3 (same frequencies), adds SPIM3 that can be @ up to 32Mbps frequency(!)
#if !defined(FASTLED_NRF52_SPIM)
#define FASTLED_NRF52_SPIM NRF_SPIM0
#endif
/* This class is slightly simpler than fastpin, as it can rely on fastpin
* to handle the mapping to the underlying PN.XX board-level pins...
*/
/// SPI_CLOCK_DIVIDER is number of CPU clock cycles per SPI transmission bit?
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class NRF52SPIOutput {
private:
// static variables -- always using same SPIM instance
static bool s_InUse;
static bool s_NeedToWait; // a data transfer was started, and completion event was not cleared.
/*
// TODO -- Workaround nRF52840 errata #198, which relates to
// contention between SPIM3 and CPU over AHB.
// The workaround is to ensure the SPIM TX buffer
// is on a different / dedicated RAM block.
// This also avoids AHB contention generally, so
// should be applied to all supported boards.
//
// But... how to allocate m_Buffer[] to be at a
// specific memory range? Also, might need to
// avoid use of single-transaction writeBytes()
// as cannot control where that memory lies....
*/
static uint8_t s_BufferIndex;
static uint8_t s_Buffer[2][2]; // 2x two-byte buffers, allows one buffer currently being sent, and a second one being prepped to send.
// This allows saving the configuration of the SPIM instance
// upon select(), and restoring the configuration upon release().
struct spim_config {
uint32_t inten;
uint32_t shorts;
uint32_t sck_pin;
uint32_t mosi_pin;
uint32_t miso_pin;
uint32_t frequency;
// data pointers, RX/TX counts not saved as would only hide bugs
uint32_t config; // mode & bit order
uint32_t orc;
#if false // additional configuration to save/restore for SPIM3
uint32_t csn_pin;
uint32_t csn_polarity; // CSNPOL
uint32_t csn_duration; // IFTIMING.CSNDUR
uint32_t rx_delay; // IFTIMING.RXDELAY
uint32_t dcx_pin; // PSELDCX
uint32_t dcx_config; // DCXCNT
#endif
} m_SpiSavedConfig;
void saveSpimConfig() {
m_SpiSavedConfig.inten = FASTLED_NRF52_SPIM->INTENSET;
m_SpiSavedConfig.shorts = FASTLED_NRF52_SPIM->SHORTS;
m_SpiSavedConfig.sck_pin = FASTLED_NRF52_SPIM->PSEL.SCK;
m_SpiSavedConfig.mosi_pin = FASTLED_NRF52_SPIM->PSEL.MOSI;
m_SpiSavedConfig.miso_pin = FASTLED_NRF52_SPIM->PSEL.MISO;
m_SpiSavedConfig.frequency = FASTLED_NRF52_SPIM->FREQUENCY;
m_SpiSavedConfig.config = FASTLED_NRF52_SPIM->CONFIG;
m_SpiSavedConfig.orc = FASTLED_NRF52_SPIM->ORC;
#if false // additional configuration to save/restore for SPIM3
m_SpiSavedConfig.csn_pin = FASTLED_NRF52_SPIM->PSEL.CSN;
m_SpiSavedConfig.csn_polarity = FASTLED_NRF52_SPIM->CSNPOL;
m_SpiSavedConfig.csn_duration = FASTLED_NRF52_SPIM->IFTIMING.CSNDUR;
m_SpiSavedConfig.dcx_pin = FASTLED_NRF52_SPIM->PSELDCX;
m_SpiSavedConfig.dcx_config = FASTLED_NRF52_SPIM->DCXCNT;
#endif
}
void restoreSpimConfig() {
// 0. ASSERT() the SPIM instance is not enabled
FASTLED_NRF52_SPIM->INTENCLR = 0xFFFFFFFF;
FASTLED_NRF52_SPIM->INTENSET = m_SpiSavedConfig.inten;
FASTLED_NRF52_SPIM->SHORTS = m_SpiSavedConfig.shorts;
FASTLED_NRF52_SPIM->PSEL.SCK = m_SpiSavedConfig.sck_pin;
FASTLED_NRF52_SPIM->PSEL.MOSI = m_SpiSavedConfig.mosi_pin;
FASTLED_NRF52_SPIM->PSEL.MISO = m_SpiSavedConfig.miso_pin;
FASTLED_NRF52_SPIM->FREQUENCY = m_SpiSavedConfig.frequency;
FASTLED_NRF52_SPIM->CONFIG = m_SpiSavedConfig.config;
FASTLED_NRF52_SPIM->ORC = m_SpiSavedConfig.orc;
#if false // additional configuration to save/restore for SPIM3
FASTLED_NRF52_SPIM->PSEL.CSN = m_SpiSavedConfig.csn_pin;
FASTLED_NRF52_SPIM->CSNPOL = m_SpiSavedConfig.csn_polarity;
FASTLED_NRF52_SPIM->IFTIMING.CSNDUR = m_SpiSavedConfig.csn_duration;
FASTLED_NRF52_SPIM->PSELDCX = m_SpiSavedConfig.dcx_pin;
FASTLED_NRF52_SPIM->DCXCNT = m_SpiSavedConfig.dcx_config;
#endif
}
public:
NRF52SPIOutput() {}
// Low frequency GPIO is for signals with a frequency up to 10 kHz. Lowest speed SPIM is 125kbps.
static_assert(!FastPin<_DATA_PIN>::LowSpeedOnlyRecommended(), "Invalid (low-speed only) pin specified");
static_assert(!FastPin<_CLOCK_PIN>::LowSpeedOnlyRecommended(), "Invalid (low-speed only) pin specified");
/// initialize the SPI subssytem
void init() {
// 0. ASSERT() the SPIM instance is not enabled / in use
//ASSERT(m_SPIM->ENABLE != (SPIM_ENABLE_ENABLE_Enabled << SPIM_ENABLE_ENABLE_Pos));
// 1. set pins to output/H0H1 drive/etc.
FastPin<_DATA_PIN>::setOutput();
FastPin<_CLOCK_PIN>::setOutput();
// 2. Configure SPIMx
nrf_spim_configure(
FASTLED_NRF52_SPIM,
NRF_SPIM_MODE_0,
NRF_SPIM_BIT_ORDER_MSB_FIRST
);
nrf_spim_frequency_set(
FASTLED_NRF52_SPIM,
NRF_SPIM_FREQ_4M // BUGBUG -- use _SPI_CLOCK_DIVIDER to determine frequency
);
nrf_spim_pins_set(
FASTLED_NRF52_SPIM,
FastPin<_CLOCK_PIN>::nrf_pin(),
FastPin<_DATA_PIN>::nrf_pin(),
NRF_SPIM_PIN_NOT_CONNECTED
);
// 4. Ensure events are cleared
nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_END);
nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_STARTED);
// 5. Enable the SPIM instance
nrf_spim_enable(FASTLED_NRF52_SPIM);
}
/// latch the CS select
void select() {
//ASSERT(!s_InUse);
saveSpimConfig();
s_InUse = true;
init();
}
/// release the CS select
void release() {
//ASSERT(s_InUse);
waitFully();
s_InUse = false;
restoreSpimConfig();
}
/// wait until all queued up data has been written
static void waitFully() {
if (!s_NeedToWait) return;
// else, need to wait for END event
while(!FASTLED_NRF52_SPIM->EVENTS_END) {};
s_NeedToWait = 0;
// only use two events in this code...
nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_END);
nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_STARTED);
return;
}
// wait only until we can add a new transaction into the registers
// (caller must still waitFully() before actually starting this next transaction)
static void wait() {
if (!s_NeedToWait) return;
while (!FASTLED_NRF52_SPIM->EVENTS_STARTED) {};
// leave the event set here... caller must waitFully() and start next transaction
return;
}
/// write a byte out via SPI (returns immediately on writing register)
static void writeByte(uint8_t b) {
wait();
// cannot use pointer to stack, so copy to m_buffer[]
uint8_t i = (s_BufferIndex ? 1u : 0u);
s_BufferIndex = !s_BufferIndex; // 1 <==> 0 swap
s_Buffer[i][0u] = b; // cannot use the stack location, so copy to a more permanent buffer...
nrf_spim_tx_buffer_set(
FASTLED_NRF52_SPIM,
&(s_Buffer[i][0u]),
1
);
waitFully();
nrf_spim_task_trigger(
FASTLED_NRF52_SPIM,
NRF_SPIM_TASK_START
);
return;
}
/// write a word out via SPI (returns immediately on writing register)
static void writeWord(uint16_t w) {
wait();
// cannot use pointer to stack, so copy to m_buffer[]
uint8_t i = (s_BufferIndex ? 1u : 0u);
s_BufferIndex = !s_BufferIndex; // 1 <==> 0 swap
s_Buffer[i][0u] = (w >> 8u); // cannot use the stack location, so copy to a more permanent buffer...
s_Buffer[i][1u] = (w & 0xFFu); // cannot use the stack location, so copy to a more permanent buffer...
nrf_spim_tx_buffer_set(
FASTLED_NRF52_SPIM,
&(s_Buffer[i][0u]),
2
);
waitFully();
nrf_spim_task_trigger(
FASTLED_NRF52_SPIM,
NRF_SPIM_TASK_START
);
return;
}
/// A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
static void writeBytesValueRaw(uint8_t value, int len) {
while (len--) { writeByte(value); }
}
/// A full cycle of writing a value for len bytes, including select, release, and waiting
void writeBytesValue(uint8_t value, int len) {
select();
writeBytesValueRaw(value, len);
waitFully();
release();
}
/// A full cycle of writing a raw block of data out, including select, release, and waiting
void writeBytes(uint8_t *data, int len) {
// This is a special-case, with no adjustment of the bytes... write them directly...
select();
wait();
nrf_spim_tx_buffer_set(
FASTLED_NRF52_SPIM,
data,
len
);
waitFully();
nrf_spim_task_trigger(
FASTLED_NRF52_SPIM,
NRF_SPIM_TASK_START
);
waitFully();
release();
}
/// A full cycle of writing a raw block of data out, including select, release, and waiting
template<class D> void writeBytes(uint8_t *data, int len) {
uint8_t * end = data + len;
select();
wait();
while(data != end) {
writeByte(D::adjust(*data++));
}
D::postBlock(len);
waitFully();
release();
}
/// specialization for DATA_NOP ...
//template<DATA_NOP> void writeBytes(uint8_t * data, int len) {
// writeBytes(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) {
// SPIM instance must be finished transmitting and then disabled
waitFully();
nrf_spim_disable(FASTLED_NRF52_SPIM);
// set the data pin to appropriate state
if (b & (1 << BIT)) {
FastPin<_DATA_PIN>::hi();
} else {
FastPin<_DATA_PIN>::lo();
}
// delay 1/2 cycle per SPI bit
delaycycles<_SPI_CLOCK_DIVIDER/2>();
FastPin<_CLOCK_PIN>::toggle();
delaycycles<_SPI_CLOCK_DIVIDER/2>();
FastPin<_CLOCK_PIN>::toggle();
// re-enable the SPIM instance
nrf_spim_enable(FASTLED_NRF52_SPIM);
}
/// write out pixel data from the given PixelController object, including select, release, and waiting
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
select();
int len = pixels.mLen;
// TODO: If user indicates a pre-allocated double-buffer,
// then process all the pixels at once into that buffer,
// then use the non-templated WriteBytes(data, len) function
// to write the entire buffer as a single SPI transaction.
while (pixels.has(1)) {
if (FLAGS & FLAG_START_BIT) {
writeBit<0>(1);
}
writeByte(D::adjust(pixels.loadAndScale0()));
writeByte(D::adjust(pixels.loadAndScale1()));
writeByte(D::adjust(pixels.loadAndScale2()));
pixels.advanceData();
pixels.stepDithering();
}
D::postBlock(len);
waitFully();
release();
}
};
// Static member definition and initialization using templates.
// see https://stackoverflow.com/questions/3229883/static-member-initialization-in-a-class-template#answer-3229919
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
bool NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_InUse = false;
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
bool NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_NeedToWait = false;
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
uint8_t NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_BufferIndex = 0;
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
uint8_t NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_Buffer[2][2] = {{0,0},{0,0}};
#endif // #ifndef FASTLED_FORCE_SOFTWARE_SPI
#endif // #ifndef __FASTPIN_ARM_NRF52_H

View File

@@ -0,0 +1,52 @@
#ifndef __LED_SYSDEFS_ARM_NRF52
#define __LED_SYSDEFS_ARM_NRF52
#define FASTLED_ARM
#ifndef F_CPU
#define F_CPU 64000000 // the NRF52 series has a 64MHz CPU
#endif
// even though CPU is at 64MHz, use the 8MHz-defined timings because...
// PWM module runs at 16MHz
// SPI0..2 runs at 8MHz
#define CLOCKLESS_FREQUENCY 16000000 // the NRF52 has EasyDMA for PWM module at 16MHz
#ifndef F_TIMER
#define F_TIMER 16000000 // the NRF52 timer is 16MHz, even though CPU is 64MHz
#endif
#if !defined(FASTLED_USE_PROGMEM)
#define FASTLED_USE_PROGMEM 0 // nRF52 series have flat memory model
#endif
#if !defined(FASTLED_ALLOW_INTERRUPTS)
#define FASTLED_ALLOW_INTERRUPTS 1
#endif
// Use PWM instance 0
// See clockless_arm_nrf52.h and (in root of library) platforms.cpp
#define FASTLED_NRF52_ENABLE_PWM_INSTANCE0
#if defined(FASTLED_NRF52_NEVER_INLINE)
#define FASTLED_NRF52_INLINE_ATTRIBUTE __attribute__((always_inline)) inline
#else
#define FASTLED_NRF52_INLINE_ATTRIBUTE __attribute__((always_inline)) inline
#endif
#include <nrf.h>
#include <nrf_spim.h> // for FastSPI
#include <nrf_pwm.h> // for Clockless
#include <nrf_nvic.h> // for Clockless / anything else using interrupts
typedef __I uint32_t RoReg;
typedef __IO uint32_t RwReg;
#define cli() __disable_irq()
#define sei() __enable_irq()
#define FASTLED_NRF52_DEBUGPRINT(format, ...)\
// do { FastLED_NRF52_DebugPrint(format, ##__VA_ARGS__); } while(0);
#endif // __LED_SYSDEFS_ARM_NRF52

View File

@@ -0,0 +1,122 @@
#ifndef __INC_CLOCKLESS_ARM_SAM_H
#define __INC_CLOCKLESS_ARM_SAM_H
FASTLED_NAMESPACE_BEGIN
// Definition for a single channel clockless controller for the sam family of arm chips, like that used in the due and rfduino
// See clockless.h for detailed info on how the template parameters are used.
#if defined(__SAM3X8E__)
#define TADJUST 0
#define TOTAL ( (T1+TADJUST) + (T2+TADJUST) + (T3+TADJUST) )
#define FASTLED_HAS_CLOCKLESS 1
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPinBB<DATA_PIN>::port_t data_t;
data_t mPinMask;
data_ptr_t mPort;
CMinWait<WAIT_TIME> mWait;
public:
virtual void init() {
FastPinBB<DATA_PIN>::setOutput();
mPinMask = FastPinBB<DATA_PIN>::mask();
mPort = FastPinBB<DATA_PIN>::port();
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mWait.wait();
if(!showRGBInternal(pixels)) {
sei(); delayMicroseconds(WAIT_TIME); cli();
showRGBInternal(pixels);
}
mWait.mark();
}
template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register uint8_t & b) {
// Make sure we don't slot into a wrapping spot, this will delay up to 12.5µs for WS2812
// bool bShift=0;
// while(VAL < (TOTAL*10)) { bShift=true; }
// if(bShift) { next_mark = (VAL-TOTAL); };
for(register uint32_t i = BITS; i > 0; --i) {
// wait to start the bit, then set the pin high
while(DUE_TIMER_VAL < next_mark);
next_mark = (DUE_TIMER_VAL+TOTAL);
*port = 1;
// how long we want to wait next depends on whether or not our bit is set to 1 or 0
if(b&0x80) {
// we're a 1, wait until there's less than T3 clocks left
while((next_mark - DUE_TIMER_VAL) > (T3));
} else {
// we're a 0, wait until there's less than (T2+T3+slop) clocks left in this bit
while((next_mark - DUE_TIMER_VAL) > (T2+T3+6+TADJUST+TADJUST));
}
*port=0;
b <<= 1;
}
}
#define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) )
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
// gcc will use register Y for the this pointer.
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
// Setup and start the clock
TC_Configure(DUE_TIMER,DUE_TIMER_CHANNEL,TC_CMR_TCCLKS_TIMER_CLOCK1);
pmc_enable_periph_clk(DUE_TIMER_ID);
TC_Start(DUE_TIMER,DUE_TIMER_CHANNEL);
register data_ptr_t port asm("r7") = FastPinBB<DATA_PIN>::port(); FORCE_REFERENCE(port);
*port = 0;
// Setup the pixel controller and load/scale the first byte
pixels.preStepFirstByteDithering();
uint8_t b = pixels.loadAndScale0();
uint32_t next_mark = (DUE_TIMER_VAL + (TOTAL));
while(pixels.has(1)) {
pixels.stepDithering();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
if(DUE_TIMER_VAL > next_mark) {
if((DUE_TIMER_VAL - next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) {
sei(); TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL); return 0;
}
}
#endif
writeBits<8+XTRA0>(next_mark, port, b);
b = pixels.loadAndScale1();
writeBits<8+XTRA0>(next_mark, port,b);
b = pixels.loadAndScale2();
writeBits<8+XTRA0>(next_mark, port,b);
b = pixels.advanceAndLoadAndScale0();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
};
TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL);
return DUE_TIMER_VAL;
}
};
#endif
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,183 @@
#ifndef __INC_BLOCK_CLOCKLESS_H
#define __INC_BLOCK_CLOCKLESS_H
FASTLED_NAMESPACE_BEGIN
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. The first point
// is where the line is raised hi. The second pointsnt is where the line is dropped low for a zero. The third point is where the
// line is dropped low for a one. T1, T2, and T3 correspond to the timings for those three in clock cycles.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if defined(__SAM3X8E__)
#define PORT_MASK (((1<<LANES)-1) & ((FIRST_PIN==2) ? 0xFF : 0xFF))
#define FASTLED_HAS_BLOCKLESS 1
#define PORTD_FIRST_PIN 25
#define PORTA_FIRST_PIN 69
#define PORTB_FIRST_PIN 90
typedef union {
uint8_t bytes[8];
uint32_t raw[2];
} Lines;
#define TADJUST 0
#define TOTAL ( (T1+TADJUST) + (T2+TADJUST) + (T3+TADJUST) )
#define T1_MARK (TOTAL - (T1+TADJUST))
#define T2_MARK (T1_MARK - (T2+TADJUST))
template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PORT_MASK> {
typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPin<FIRST_PIN>::port_t data_t;
data_t mPinMask;
data_ptr_t mPort;
CMinWait<WAIT_TIME> mWait;
public:
virtual int size() { return CLEDController::size() * LANES; }
virtual void init() {
static_assert(LANES <= 8, "Maximum of 8 lanes for Due parallel controllers!");
if(FIRST_PIN == PORTA_FIRST_PIN) {
switch(LANES) {
case 8: FastPin<31>::setOutput();
case 7: FastPin<58>::setOutput();
case 6: FastPin<100>::setOutput();
case 5: FastPin<59>::setOutput();
case 4: FastPin<60>::setOutput();
case 3: FastPin<61>::setOutput();
case 2: FastPin<68>::setOutput();
case 1: FastPin<69>::setOutput();
}
} else if(FIRST_PIN == PORTD_FIRST_PIN) {
switch(LANES) {
case 8: FastPin<11>::setOutput();
case 7: FastPin<29>::setOutput();
case 6: FastPin<15>::setOutput();
case 5: FastPin<14>::setOutput();
case 4: FastPin<28>::setOutput();
case 3: FastPin<27>::setOutput();
case 2: FastPin<26>::setOutput();
case 1: FastPin<25>::setOutput();
}
} else if(FIRST_PIN == PORTB_FIRST_PIN) {
switch(LANES) {
case 8: FastPin<97>::setOutput();
case 7: FastPin<96>::setOutput();
case 6: FastPin<95>::setOutput();
case 5: FastPin<94>::setOutput();
case 4: FastPin<93>::setOutput();
case 3: FastPin<92>::setOutput();
case 2: FastPin<91>::setOutput();
case 1: FastPin<90>::setOutput();
}
}
mPinMask = FastPin<FIRST_PIN>::mask();
mPort = FastPin<FIRST_PIN>::port();
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
virtual void showPixels(PixelController<RGB_ORDER, LANES, PORT_MASK> & pixels) {
mWait.wait();
showRGBInternal(pixels);
sei();
mWait.mark();
}
static uint32_t showRGBInternal(PixelController<RGB_ORDER, LANES, PORT_MASK> &allpixels) {
// Serial.println("Entering show");
int nLeds = allpixels.mLen;
// Setup the pixel controller and load/scale the first byte
Lines b0,b1,b2;
allpixels.preStepFirstByteDithering();
for(uint8_t i = 0; i < LANES; i++) {
b0.bytes[i] = allpixels.loadAndScale0(i);
}
// Setup and start the clock
TC_Configure(DUE_TIMER,DUE_TIMER_CHANNEL,TC_CMR_TCCLKS_TIMER_CLOCK1);
pmc_enable_periph_clk(DUE_TIMER_ID);
TC_Start(DUE_TIMER,DUE_TIMER_CHANNEL);
#if (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
#endif
uint32_t next_mark = (DUE_TIMER_VAL + (TOTAL));
while(nLeds--) {
allpixels.stepDithering();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
if(DUE_TIMER_VAL > next_mark) {
if((DUE_TIMER_VAL - next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) {
sei(); TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL); return DUE_TIMER_VAL;
}
}
#endif
// Write first byte, read next byte
writeBits<8+XTRA0,1>(next_mark, b0, b1, allpixels);
// Write second byte, read 3rd byte
writeBits<8+XTRA0,2>(next_mark, b1, b2, allpixels);
allpixels.advanceData();
// Write third byte
writeBits<8+XTRA0,0>(next_mark, b2, b0, allpixels);
#if (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
}
return DUE_TIMER_VAL;
}
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, Lines & b3, PixelController<RGB_ORDER,LANES, PORT_MASK> &pixels) { // , register uint32_t & b2) {
Lines b2;
transpose8x1(b.bytes,b2.bytes);
register uint8_t d = pixels.template getd<PX>(pixels);
register uint8_t scale = pixels.template getscale<PX>(pixels);
for(uint32_t i = 0; (i < LANES) && (i<8); i++) {
while(DUE_TIMER_VAL < next_mark);
next_mark = (DUE_TIMER_VAL+TOTAL);
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
while((next_mark - DUE_TIMER_VAL) > (T2+T3+6));
*FastPin<FIRST_PIN>::cport() = (~b2.bytes[7-i]) & PORT_MASK;
while((next_mark - (DUE_TIMER_VAL)) > T3);
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
b3.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
}
for(uint32_t i = LANES; i < 8; i++) {
while(DUE_TIMER_VAL < next_mark);
next_mark = (DUE_TIMER_VAL+TOTAL);
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
while((next_mark - DUE_TIMER_VAL) > (T2+T3+6));
*FastPin<FIRST_PIN>::cport() = (~b2.bytes[7-i]) & PORT_MASK;
while((next_mark - DUE_TIMER_VAL) > T3);
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
}
}
};
#endif
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,10 @@
#ifndef __INC_FASTLED_ARM_SAM_H
#define __INC_FASTLED_ARM_SAM_H
// Include the sam headers
#include "fastpin_arm_sam.h"
#include "fastspi_arm_sam.h"
#include "clockless_arm_sam.h"
#include "clockless_block_arm_sam.h"
#endif

View File

@@ -0,0 +1,137 @@
#ifndef __INC_FASTPIN_ARM_SAM_H
#define __INC_FASTPIN_ARM_SAM_H
FASTLED_NAMESPACE_BEGIN
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
#warning "Software pin support forced, pin access will be sloightly slower."
#define NO_HARDWARE_PIN_SUPPORT
#undef HAS_HARDWARE_PIN_SUPPORT
#else
/// Template definition for arduino due style ARM pins, providing direct access to the various GPIO registers. Note that this
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
/// The registers are data register, set output register, clear output register, set data direction register
template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PDDR> class _DUEPIN {
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_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
};
/// Template definition for DUE style ARM pins using bit banding, providing direct access to the various GPIO registers. GCC
/// 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 GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))
#define _R(T) struct __gen_struct_ ## T
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } \
template<int BIT> static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
#define _FL_IO(L,C) _RD32(REG_PIO ## L ## _ODSR); _RD32(REG_PIO ## L ## _SODR); _RD32(REG_PIO ## L ## _CODR); _RD32(REG_PIO ## L ## _OER); _FL_DEFINE_PORT3(L, C, _R(REG_PIO ## L ## _ODSR));
#define _FL_DEFPIN(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)> {};
_FL_IO(A,0);
_FL_IO(B,1);
_FL_IO(C,2);
_FL_IO(D,3);
#if defined(__SAM3X8E__)
#define MAX_PIN 78
_FL_DEFPIN(0, 8, A); _FL_DEFPIN(1, 9, A); _FL_DEFPIN(2, 25, B); _FL_DEFPIN(3, 28, C);
_FL_DEFPIN(4, 26, C); _FL_DEFPIN(5, 25, C); _FL_DEFPIN(6, 24, C); _FL_DEFPIN(7, 23, C);
_FL_DEFPIN(8, 22, C); _FL_DEFPIN(9, 21, C); _FL_DEFPIN(10, 29, C); _FL_DEFPIN(11, 7, D);
_FL_DEFPIN(12, 8, D); _FL_DEFPIN(13, 27, B); _FL_DEFPIN(14, 4, D); _FL_DEFPIN(15, 5, D);
_FL_DEFPIN(16, 13, A); _FL_DEFPIN(17, 12, A); _FL_DEFPIN(18, 11, A); _FL_DEFPIN(19, 10, A);
_FL_DEFPIN(20, 12, B); _FL_DEFPIN(21, 13, B); _FL_DEFPIN(22, 26, B); _FL_DEFPIN(23, 14, A);
_FL_DEFPIN(24, 15, A); _FL_DEFPIN(25, 0, D); _FL_DEFPIN(26, 1, D); _FL_DEFPIN(27, 2, D);
_FL_DEFPIN(28, 3, D); _FL_DEFPIN(29, 6, D); _FL_DEFPIN(30, 9, D); _FL_DEFPIN(31, 7, A);
_FL_DEFPIN(32, 10, D); _FL_DEFPIN(33, 1, C); _FL_DEFPIN(34, 2, C); _FL_DEFPIN(35, 3, C);
_FL_DEFPIN(36, 4, C); _FL_DEFPIN(37, 5, C); _FL_DEFPIN(38, 6, C); _FL_DEFPIN(39, 7, C);
_FL_DEFPIN(40, 8, C); _FL_DEFPIN(41, 9, C); _FL_DEFPIN(42, 19, A); _FL_DEFPIN(43, 20, A);
_FL_DEFPIN(44, 19, C); _FL_DEFPIN(45, 18, C); _FL_DEFPIN(46, 17, C); _FL_DEFPIN(47, 16, C);
_FL_DEFPIN(48, 15, C); _FL_DEFPIN(49, 14, C); _FL_DEFPIN(50, 13, C); _FL_DEFPIN(51, 12, C);
_FL_DEFPIN(52, 21, B); _FL_DEFPIN(53, 14, B); _FL_DEFPIN(54, 16, A); _FL_DEFPIN(55, 24, A);
_FL_DEFPIN(56, 23, A); _FL_DEFPIN(57, 22, A); _FL_DEFPIN(58, 6, A); _FL_DEFPIN(59, 4, A);
_FL_DEFPIN(60, 3, A); _FL_DEFPIN(61, 2, A); _FL_DEFPIN(62, 17, B); _FL_DEFPIN(63, 18, B);
_FL_DEFPIN(64, 19, B); _FL_DEFPIN(65, 20, B); _FL_DEFPIN(66, 15, B); _FL_DEFPIN(67, 16, B);
_FL_DEFPIN(68, 1, A); _FL_DEFPIN(69, 0, A); _FL_DEFPIN(70, 17, A); _FL_DEFPIN(71, 18, A);
_FL_DEFPIN(72, 30, C); _FL_DEFPIN(73, 21, A); _FL_DEFPIN(74, 25, A); _FL_DEFPIN(75, 26, A);
_FL_DEFPIN(76, 27, A); _FL_DEFPIN(77, 28, A); _FL_DEFPIN(78, 23, B);
// digix pins
_FL_DEFPIN(90, 0, B); _FL_DEFPIN(91, 1, B); _FL_DEFPIN(92, 2, B); _FL_DEFPIN(93, 3, B);
_FL_DEFPIN(94, 4, B); _FL_DEFPIN(95, 5, B); _FL_DEFPIN(96, 6, B); _FL_DEFPIN(97, 7, B);
_FL_DEFPIN(98, 8, B); _FL_DEFPIN(99, 9, B); _FL_DEFPIN(100, 5, A); _FL_DEFPIN(101, 22, B);
_FL_DEFPIN(102, 23, B); _FL_DEFPIN(103, 24, B); _FL_DEFPIN(104, 27, C); _FL_DEFPIN(105, 20, C);
_FL_DEFPIN(106, 11, C); _FL_DEFPIN(107, 10, C); _FL_DEFPIN(108, 21, A); _FL_DEFPIN(109, 30, C);
_FL_DEFPIN(110, 29, B); _FL_DEFPIN(111, 30, B); _FL_DEFPIN(112, 31, B); _FL_DEFPIN(113, 28, B);
#define SPI_DATA 75
#define SPI_CLOCK 76
#define ARM_HARDWARE_SPI
#define HAS_HARDWARE_PIN_SUPPORT
#endif
#endif // FASTLED_FORCE_SOFTWARE_PINS
FASTLED_NAMESPACE_END
#endif // __INC_FASTPIN_ARM_SAM_H

View File

@@ -0,0 +1,163 @@
#ifndef __INC_FASTSPI_ARM_SAM_H
#define __INC_FASTSPI_ARM_SAM_H
FASTLED_NAMESPACE_BEGIN
#if defined(__SAM3X8E__)
#define m_SPI ((Spi*)SPI0)
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_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
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,39 @@
#ifndef __INC_LED_SYSDEFS_ARM_SAM_H
#define __INC_LED_SYSDEFS_ARM_SAM_H
#define FASTLED_ARM
// Setup DUE timer defines/channels/etc...
#ifndef DUE_TIMER_CHANNEL
#define DUE_TIMER_GROUP 0
#endif
#ifndef DUE_TIMER_CHANNEL
#define DUE_TIMER_CHANNEL 0
#endif
#define DUE_TIMER ((DUE_TIMER_GROUP==0) ? TC0 : ((DUE_TIMER_GROUP==1) ? TC1 : TC2))
#define DUE_TIMER_ID (ID_TC0 + (DUE_TIMER_GROUP*3) + DUE_TIMER_CHANNEL)
#define DUE_TIMER_VAL (DUE_TIMER->TC_CHANNEL[DUE_TIMER_CHANNEL].TC_CV << 1)
#define DUE_TIMER_RUNNING ((DUE_TIMER->TC_CHANNEL[DUE_TIMER_CHANNEL].TC_SR & TC_SR_CLKSTA) != 0)
#ifndef INTERRUPT_THRESHOLD
#define INTERRUPT_THRESHOLD 1
#endif
// Default to allowing interrupts
#ifndef FASTLED_ALLOW_INTERRUPTS
#define FASTLED_ALLOW_INTERRUPTS 1
#endif
#if FASTLED_ALLOW_INTERRUPTS == 1
#define FASTLED_ACCURATE_CLOCK
#endif
// reusing/abusing cli/sei defs for due
#define cli() __disable_irq(); __disable_fault_irq();
#define sei() __enable_irq(); __enable_fault_irq();
#endif

View File

@@ -0,0 +1,126 @@
#ifndef __INC_CLOCKLESS_ARM_STM32_H
#define __INC_CLOCKLESS_ARM_STM32_H
FASTLED_NAMESPACE_BEGIN
// Definition for a single channel clockless controller for the stm32 family of chips, like that used in the spark core
// See clockless.h for detailed info on how the template parameters are used.
#define FASTLED_HAS_CLOCKLESS 1
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPin<DATA_PIN>::port_t data_t;
data_t mPinMask;
data_ptr_t mPort;
CMinWait<WAIT_TIME> mWait;
public:
virtual void init() {
FastPin<DATA_PIN>::setOutput();
mPinMask = FastPin<DATA_PIN>::mask();
mPort = FastPin<DATA_PIN>::port();
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
protected:
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mWait.wait();
if(!showRGBInternal(pixels)) {
sei(); delayMicroseconds(WAIT_TIME); cli();
showRGBInternal(pixels);
}
mWait.mark();
}
#define _CYCCNT (*(volatile uint32_t*)(0xE0001004UL))
template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t & b) {
for(register uint32_t i = BITS-1; i > 0; --i) {
while(_CYCCNT < (T1+T2+T3-20));
FastPin<DATA_PIN>::fastset(port, hi);
_CYCCNT = 4;
if(b&0x80) {
while(_CYCCNT < (T1+T2-20));
FastPin<DATA_PIN>::fastset(port, lo);
} else {
while(_CYCCNT < (T1-10));
FastPin<DATA_PIN>::fastset(port, lo);
}
b <<= 1;
}
while(_CYCCNT < (T1+T2+T3-20));
FastPin<DATA_PIN>::fastset(port, hi);
_CYCCNT = 4;
if(b&0x80) {
while(_CYCCNT < (T1+T2-20));
FastPin<DATA_PIN>::fastset(port, lo);
} else {
while(_CYCCNT < (T1-10));
FastPin<DATA_PIN>::fastset(port, lo);
}
}
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
// gcc will use register Y for the this pointer.
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
// Get access to the clock
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
DWT->CYCCNT = 0;
register data_ptr_t port = FastPin<DATA_PIN>::port();
register data_t hi = *port | FastPin<DATA_PIN>::mask();;
register data_t lo = *port & ~FastPin<DATA_PIN>::mask();;
*port = lo;
// Setup the pixel controller and load/scale the first byte
pixels.preStepFirstByteDithering();
register uint8_t b = pixels.loadAndScale0();
cli();
uint32_t next_mark = (T1+T2+T3);
DWT->CYCCNT = 0;
while(pixels.has(1)) {
pixels.stepDithering();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
cli();
// if interrupts took longer than 45µs, punt on the current frame
if(DWT->CYCCNT > next_mark) {
if((DWT->CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
}
hi = *port | FastPin<DATA_PIN>::mask();
lo = *port & ~FastPin<DATA_PIN>::mask();
#endif
// Write first byte, read next byte
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.loadAndScale1();
// Write second byte, read 3rd byte
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.loadAndScale2();
// Write third byte, read 1st byte of next pixel
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
b = pixels.advanceAndLoadAndScale0();
#if (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
};
sei();
return DWT->CYCCNT;
}
};
FASTLED_NAMESPACE_END
#endif

View File

@@ -0,0 +1,63 @@
#ifndef __CM3_REGS
#define __CM3_REGS
#include <stdint.h>
#ifdef __cplusplus
#define __I volatile /*!< Defines 'read only' permissions */
#else
#define __I volatile const /*!< Defines 'read only' permissions */
#endif
#define __O volatile /*!< Defines 'write only' permissions */
#define __IO volatile /*!< Defines 'read / write' permissions */
typedef struct
{
__IO uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */
__O uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */
__IO uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */
__IO uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */
} CoreDebug_Type;
#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */
#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */
#define CoreDebug_DEMCR_TRCENA_Pos 24 /*!< CoreDebug DEMCR: TRCENA Position */
#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */
typedef struct
{
__IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */
__IO uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */
__IO uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */
__IO uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */
__IO uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */
__IO uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */
__IO uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */
__I uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */
__IO uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */
__IO uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */
__IO uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */
uint32_t RESERVED0[1];
__IO uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */
__IO uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */
__IO uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */
uint32_t RESERVED1[1];
__IO uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */
__IO uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */
__IO uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */
uint32_t RESERVED2[1];
__IO uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */
__IO uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */
__IO uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */
} DWT_Type;
#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */
#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */
#define DWT_CTRL_CYCCNTENA_Pos 0 /*!< DWT CTRL: CYCCNTENA Position */
#define DWT_CTRL_CYCCNTENA_Msk (0x1UL << DWT_CTRL_CYCCNTENA_Pos) /*!< DWT CTRL: CYCCNTENA Mask */
#endif // __CM3_REGS

View File

@@ -0,0 +1,9 @@
#ifndef __INC_FASTLED_ARM_SAM_H
#define __INC_FASTLED_ARM_SAM_H
// Include the sam headers
#include "fastpin_arm_stm32.h"
// #include "fastspi_arm_stm32.h"
#include "clockless_arm_stm32.h"
#endif

Some files were not shown because too many files have changed in this diff Show More