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

github.com/FastLED/FastLED.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FastLED.h3
-rw-r--r--block_clockless.h4
-rw-r--r--clockless.h503
-rw-r--r--clockless2.h6
-rw-r--r--clockless_arm_k20.h139
-rw-r--r--clockless_arm_sam.h213
-rw-r--r--clockless_avr.h173
-rw-r--r--clockless_trinket.h3
8 files changed, 536 insertions, 508 deletions
diff --git a/FastLED.h b/FastLED.h
index b3ef74d3..89e94d92 100644
--- a/FastLED.h
+++ b/FastLED.h
@@ -5,9 +5,6 @@
#include "fastpin.h"
#include "fastspi.h"
#include "clockless.h"
-#include "block_clockless.h"
-#include "clockless2.h"
-#include "clockless_trinket.h"
#include "lib8tion.h"
#include "hsv2rgb.h"
#include "chipsets.h"
diff --git a/block_clockless.h b/block_clockless.h
index beb742a9..350e0d65 100644
--- a/block_clockless.h
+++ b/block_clockless.h
@@ -14,10 +14,6 @@
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Convinience macros to wrap around the toggling of hi vs. lo
-#define SET_LO FLIP ? FastPin<DATA_PIN>::fastset(port, hi) : FastPin<DATA_PIN>::fastset(port, lo);
-#define SET_HI FLIP ? FastPin<DATA_PIN>::fastset(port, lo) : FastPin<DATA_PIN>::fastset(port, hi);
-
#define PORT_MASK 0x77EFF3FE
#define SKIPLIST ~PORT_MASK
diff --git a/clockless.h b/clockless.h
index ae6e8917..c7d3b49f 100644
--- a/clockless.h
+++ b/clockless.h
@@ -18,503 +18,10 @@
#define SET_LO FLIP ? FastPin<DATA_PIN>::fastset(port, hi) : FastPin<DATA_PIN>::fastset(port, lo);
#define SET_HI FLIP ? FastPin<DATA_PIN>::fastset(port, lo) : FastPin<DATA_PIN>::fastset(port, hi);
-#ifdef FASTLED_AVR
-template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, bool FLIP = false, int WAIT_TIME = 50>
-class ClocklessController : public CLEDController {
- typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
- typedef typename FastPin<DATA_PIN>::port_t data_t;
-
- data_t mPinMask;
- data_ptr_t mPort;
- CMinWait<WAIT_TIME> mWait;
-public:
- virtual void init() {
- FastPin<DATA_PIN>::setOutput();
- mPinMask = FastPin<DATA_PIN>::mask();
- mPort = FastPin<DATA_PIN>::port();
- }
-
- template <int N, int ADJ>inline static void bitSetLast(register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t b) {
- // First cycle
- SET_HI; // 1 clock cycle if using out, 2 otherwise
- delaycycles<T1 - (_CYCLES(DATA_PIN) + 1)>(); // 1st cycle length minus 1 clock for out, 1 clock for sbrs
- __asm__ __volatile__ ("sbrs %0, %1" :: "r" (b), "M" (N) :); // 1 clock for check (+1 if skipping, next op is also 1 clock)
-
- // Second cycle
- SET_LO; // 1/2 clock cycle if using out
- delaycycles<T2 - _CYCLES(DATA_PIN)>(); // 2nd cycle length minus 1/2 clock for out
-
- // Third cycle
- SET_LO; // 1/2 clock cycle if using out
- delaycycles<T3 - (_CYCLES(DATA_PIN) + ADJ)>(); // 3rd cycle length minus 7 clocks for out, loop compare, jump, next uint8_t load
- }
-
- virtual void clearLeds(int nLeds) {
- showColor(CRGB(0, 0, 0), nLeds, 0);
- }
-
- // set all the leds on the controller to a given color
- virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
-
- showRGBInternal<0, false>(nLeds, scale, (const byte*)&data);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- sei();
- mWait.mark();
- }
-
- virtual void show(const struct CRGB *rgbdata, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
-
- showRGBInternal<0, true>(nLeds, scale, (const byte*)rgbdata);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- sei();
- mWait.mark();
- }
-
-#ifdef SUPPORT_ARGB
- virtual void show(const struct CARGB *rgbdata, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
-
- showRGBInternal<1, true>((long)nLeds, scale, (const byte*)rgbdata);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- sei();
- mWait.mark();
- }
-#endif
-
- // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
- // gcc will use register Y for the this pointer.
- template<int SKIP, bool ADVANCE> static void showRGBInternal(register int nLeds, register uint8_t scale, register const byte *rgbdata) {
- register byte *data = (byte*)rgbdata;
- register data_ptr_t port = FastPin<DATA_PIN>::port();
- nLeds *= (3 + SKIP);
- register uint8_t *end = data + nLeds;
- register data_t hi = FastPin<DATA_PIN>::hival();
- register data_t lo = FastPin<DATA_PIN>::loval();;
- *port = lo;
-
- register uint8_t b;
-
- if(ADVANCE) {
- b = data[SKIP + RGB_BYTE0(RGB_ORDER)];
- } else {
- b = rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
- }
- b = scale8_LEAVING_R1_DIRTY(b, scale);
-
- register uint8_t c;
- register uint8_t d;
- while(data < end) {
- for(register byte x=5; x; x--) {
- bitSetLast<7, 4>(port, hi, lo, b);
- b <<= 1;
- }
- delaycycles<1>();
- // Leave an extra 2 clocks for the next byte load
- bitSetLast<7, 1>(port, hi, lo, b);
- delaycycles<1>();
-
- // Leave an extra 4 clocks for the scale
- bitSetLast<6, 6>(port, hi, lo, b);
- if(ADVANCE) {
- c = data[SKIP + RGB_BYTE1(RGB_ORDER)];
- } else {
- c = rgbdata[SKIP + RGB_BYTE1(RGB_ORDER)];
- delaycycles<1>();
- }
- INLINE_SCALE(c, scale);
- bitSetLast<5, 1>(port, hi, lo, b);
-
- for(register byte x=5; x; x--) {
- bitSetLast<7, 4>(port, hi, lo, c);
- c <<= 1;
- }
- delaycycles<1>();
- // Leave an extra 2 clocks for the next byte load
- bitSetLast<7, 1>(port, hi, lo, c);
- delaycycles<1>();
-
- // Leave an extra 4 clocks for the scale
- bitSetLast<6, 6>(port, hi, lo, c);
- if(ADVANCE) {
- d = data[SKIP + RGB_BYTE2(RGB_ORDER)];
- } else {
- d = rgbdata[SKIP + RGB_BYTE2(RGB_ORDER)];
- delaycycles<1>();
- }
- INLINE_SCALE(d, scale);
- bitSetLast<5, 1>(port, hi, lo, c);
-
- for(register byte x=5; x; x--) {
- bitSetLast<7, 4>(port, hi, lo, d);
- d <<= 1;
- }
- delaycycles<1>();
- // Leave an extra 2 clocks for the next byte load
- bitSetLast<7, 2>(port, hi, lo, d);
- data += (SKIP + 3);
- // Leave an extra 4 clocks for the scale
- bitSetLast<6, 6>(port, hi, lo, d);
- if(ADVANCE) {
- b = data[SKIP + RGB_BYTE0(RGB_ORDER)];
- } else {
- b = rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
- delaycycles<1>();
- }
- INLINE_SCALE(b, scale);
- bitSetLast<5, 6>(port, hi, lo, d);
- }
- cleanup_R1();
- }
-};
-
-#elif defined(FASTLED_TEENSY3)
-template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, bool FLIP = false, int WAIT_TIME = 50>
-class ClocklessController : public CLEDController {
- typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
- typedef typename FastPin<DATA_PIN>::port_t data_t;
-
- data_t mPinMask;
- data_ptr_t mPort;
- CMinWait<WAIT_TIME> mWait;
-public:
- virtual void init() {
- FastPin<DATA_PIN>::setOutput();
- mPinMask = FastPin<DATA_PIN>::mask();
- mPort = FastPin<DATA_PIN>::port();
- }
-
- virtual void clearLeds(int nLeds) {
- showColor(CRGB(0, 0, 0), nLeds, 0);
- }
-
- // set all the leds on the controller to a given color
- virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
-
- showRGBInternal<0, false>(nLeds, scale, (const byte*)&data);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- sei();
- mWait.mark();
- }
-
- virtual void show(const struct CRGB *rgbdata, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
-
- showRGBInternal<0, true>(nLeds, scale, (const byte*)rgbdata);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- sei();
- mWait.mark();
- }
-
-#ifdef SUPPORT_ARGB
- virtual void show(const struct CARGB *rgbdata, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
-
- showRGBInternal<1, true>(nLeds, scale, (const byte*)rgbdata);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- sei();
- mWait.mark();
- }
-#endif
-
- inline static void write8Bits(register data_ptr_t port, register data_t hi, register data_t lo, register uint32_t & b) __attribute__ ((always_inline)) {
- // TODO: hand rig asm version of this method. The timings are based on adjusting/studying GCC compiler ouptut. This
- // will bite me in the ass at some point, I know it.
- for(register uint32_t i = 7; i > 0; i--) {
- FastPin<DATA_PIN>::fastset(port, hi);
- delaycycles<T1 - 5>(); // 5 cycles - 2 store, 1 and, 1 test, 1 if
- if(b & 0x80) { FastPin<DATA_PIN>::fastset(port, hi); } else { FastPin<DATA_PIN>::fastset(port, lo); }
- b <<= 1;
- delaycycles<T2 - 2>(); // 2 cycles, 1 store/skip, 1 shift
- FastPin<DATA_PIN>::fastset(port, lo);
- delaycycles<T3 - 5>(); // 3 cycles, 2 store, 1 sub, 1 branch backwards
- }
- // delay an extra cycle because falling out of the loop takes on less cycle than looping around
- delaycycles<1>();
-
- FastPin<DATA_PIN>::fastset(port, hi);
- delaycycles<T1 - 6>();
- if(b & 0x80) { FastPin<DATA_PIN>::fastset(port, hi); } else { FastPin<DATA_PIN>::fastset(port, lo); }
- delaycycles<T2 - 2>(); // 4 cycles, 2 store, store/skip
- FastPin<DATA_PIN>::fastset(port, lo);
- }
-
- // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
- // gcc will use register Y for the this pointer.
- template<int SKIP, bool ADVANCE> static void showRGBInternal(register int nLeds, register uint8_t scale, register const byte *rgbdata) {
- register byte *data = (byte*)rgbdata;
- register data_t mask = FastPin<DATA_PIN>::mask();
- register data_ptr_t port = FastPin<DATA_PIN>::port();
- nLeds *= (3 + SKIP);
- register uint8_t *end = data + nLeds;
- register data_t hi = *port | mask;
- register data_t lo = *port & ~mask;
- *port = lo;
-
- register uint32_t b;
- b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE0(RGB_ORDER)];
- b = scale8(b, scale);
- while(data < end) {
- // Write first byte, read next byte
- write8Bits(port, hi, lo, b);
-
- b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE1(RGB_ORDER)];
- INLINE_SCALE(b, scale);
- delaycycles<T3 - 5>(); // 1 store, 2 load, 1 mul, 1 shift,
-
- // Write second byte
- write8Bits(port, hi, lo, b);
-
- b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE2(RGB_ORDER)];
- INLINE_SCALE(b, scale);
-
- data += 3 + SKIP;
- if((RGB_ORDER & 0070) == 0) {
- delaycycles<T3 - 6>(); // 1 store, 2 load, 1 mul, 1 shift, 1 adds if BRG or GRB
- } else {
- delaycycles<T3 - 5>(); // 1 store, 2 load, 1 mul, 1 shift,
- }
-
- // Write third byte
- write8Bits(port, hi, lo, b);
-
- b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE0(RGB_ORDER)];
- INLINE_SCALE(b, scale);
-
- delaycycles<T3 - 11>(); // 1 store, 2 load (with increment), 1 mul, 1 shift, 1 cmp, 1 branch backwards, 1 movim
- };
- }
-};
-#elif defined(__SAM3X8E__)
-
-template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, bool FLIP = false, int WAIT_TIME = 50>
-class ClocklessController : public CLEDController {
- typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
- typedef typename FastPinBB<DATA_PIN>::port_t data_t;
-
- data_t mPinMask;
- data_ptr_t mPort;
- CMinWait<WAIT_TIME> mWait;
-public:
- virtual void init() {
- FastPinBB<DATA_PIN>::setOutput();
- mPinMask = FastPinBB<DATA_PIN>::mask();
- mPort = FastPinBB<DATA_PIN>::port();
- }
-
- virtual void clearLeds(int nLeds) {
- showColor(CRGB(0, 0, 0), nLeds, 0);
- }
-
- // set all the leds on the controller to a given color
- virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
- SysClockSaver savedClock(T1 + T2 + T3);
-
- showRGBInternal<0, false>(nLeds, scale, (const byte*)&data);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- savedClock.restore();
- sei();
- mWait.mark();
- }
-
- virtual void show(const struct CRGB *rgbdata, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
- SysClockSaver savedClock(T1 + T2 + T3);
-
- // FastPinBB<DATA_PIN>::hi(); delay(1); FastPinBB<DATA_PIN>::lo();
- showRGBInternal<0, true>(nLeds, scale, (const byte*)rgbdata);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- savedClock.restore();
- sei();
- mWait.mark();
- }
-
-#ifdef SUPPORT_ARGB
- virtual void show(const struct CARGB *rgbdata, int nLeds, uint8_t scale = 255) {
- mWait.wait();
- cli();
- SysClockSaver savedClock(T1 + T2 + T3);
-
- showRGBInternal<1, true>(nLeds, scale, (const byte*)rgbdata);
-
- // Adjust the timer
- long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
- MS_COUNTER += (microsTaken / 1000);
- savedClock.restore();
- sei();
- mWait.mark();
- }
-#endif
-
-// I hate using defines for these, should find a better representation at some point
-#define _CTRL CTPTR[0]
-#define _LOAD CTPTR[1]
-#define _VAL CTPTR[2]
-
- __attribute__((always_inline)) static inline void wait_loop_start(register volatile uint32_t *CTPTR) {
- __asm__ __volatile__ (
- "L_%=: ldr.w r8, [%0]\n"
- " tst.w r8, #65536\n"
- " beq.n L_%=\n"
- : /* no outputs */
- : "r" (CTPTR)
- : "r8"
- );
- }
-
- template<int MARK> __attribute__((always_inline)) static inline void wait_loop_mark(register volatile uint32_t *CTPTR) {
- __asm__ __volatile__ (
- "L_%=: ldr.w r8, [%0, #8]\n"
- " cmp.w r8, %1\n"
- " bhi.n L_%=\n"
- : /* no outputs */
- : "r" (CTPTR), "I" (MARK)
- : "r8"
- );
- }
-
- __attribute__((always_inline)) static inline void mark_port(register data_ptr_t port, register int val) {
- __asm__ __volatile__ (
- " str.w %0, [%1]\n"
- : /* no outputs */
- : "r" (val), "r" (port)
- );
- }
-#define AT_BIT_START(X) wait_loop_start(CTPTR); X;
-#define AT_MARK(X) wait_loop_mark<T1_MARK>(CTPTR); { X; }
-#define AT_END(X) wait_loop_mark<T2_MARK>(CTPTR); { X; }
-
-// #define AT_BIT_START(X) while(!(_CTRL & SysTick_CTRL_COUNTFLAG_Msk)); { X; }
-// #define AT_MARK(X) while(_VAL > T1_MARK); { X; }
-// #define AT_END(X) while(_VAL > T2_MARK); { X; }
-
-//#define AT_MARK(X) delayclocks_until<T1_MARK>(_VAL); X;
-//#define AT_END(X) delayclocks_until<T2_MARK>(_VAL); X;
-
-#define TOTAL (T1 + T2 + T3)
-
-#define T1_MARK (TOTAL - T1)
-#define T2_MARK (T1_MARK - T2)
- template<int MARK> __attribute__((always_inline)) static inline void delayclocks_until(register byte b) {
- __asm__ __volatile__ (
- " sub %0, %0, %1\n"
- "L_%=: subs %0, %0, #2\n"
- " bcs.n L_%=\n"
- : /* no outputs */
- : "r" (b), "I" (MARK)
- : /* no clobbers */
- );
-
- }
-
-#define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) )
-
- // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
- // gcc will use register Y for the this pointer.
- template<int SKIP, bool ADVANCE> static void showRGBInternal(register int nLeds, register uint8_t scale, register const byte *rgbdata) {
- register data_ptr_t port asm("r7") = FastPinBB<DATA_PIN>::port(); FORCE_REFERENCE(port);
- register byte *data = (byte*)rgbdata;
- register uint8_t *end = data + (nLeds*3 + SKIP);
-
- register volatile uint32_t *CTPTR asm("r6")= &SysTick->CTRL; FORCE_REFERENCE(CTPTR);
-
- *port = 0;
-
- register uint32_t b;
- b = ADVANCE ? data[SKIP + RGB_BYTE0(RGB_ORDER)] :rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
- b = scale8(b, scale);
-
- // Setup and start the clock
- _LOAD = TOTAL;
- _VAL = 0;
- _CTRL |= SysTick_CTRL_CLKSOURCE_Msk;
- _CTRL |= SysTick_CTRL_ENABLE_Msk;
-
- // read to clear the loop flag
- _CTRL;
-
- while(data < end) {
- for(register uint32_t i = 7; i > 0; i--) {
- AT_BIT_START(*port = 1);
- if(b& 0x80) {} else { AT_MARK(*port = 0); }
- AT_END(*port = 0);
- b <<= 1;
- }
-
- AT_BIT_START(*port = 1);
- if(b& 0x80) {} else { AT_MARK(*port = 0); }
- AT_END(*port = 0);
-
- b = ADVANCE ? data[SKIP + RGB_BYTE1(RGB_ORDER)] : rgbdata[SKIP + RGB_BYTE1(RGB_ORDER)];
- b = scale8(b, scale);
-
- for(register uint32_t i = 7; i > 0; i--) {
- AT_BIT_START(*port = 1);
- if(b& 0x80) {} else { AT_MARK(*port = 0); }
- AT_END(*port = 0);
- b <<= 1;
- }
-
- AT_BIT_START(*port = 1);
- if(b& 0x80) {} else { AT_MARK(*port = 0); }
- AT_END(*port = 0);
-
- b = ADVANCE ? data[SKIP + RGB_BYTE2(RGB_ORDER)] : rgbdata[SKIP + RGB_BYTE2(RGB_ORDER)];
- b = scale8(b, scale);
- data += (3 + SKIP);
-
- for(register uint32_t i = 7; i > 0; i--) {
- AT_BIT_START(*port = 1);
- if(b& 0x80) {} else { AT_MARK(*port = 0); }
- AT_END(*port = 0);
- b <<= 1;
- }
-
- AT_BIT_START(*port = 1);
- if(b& 0x80) {} else { AT_MARK(*port = 0); }
- AT_END(*port = 0);
-
- b = ADVANCE ? data[SKIP + RGB_BYTE0(RGB_ORDER)] : rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
- b = scale8(b, scale);
- };
- }
-};
-
-#endif
+#include "clockless_avr.h"
+#include "clockless_arm_k20.h"
+#include "clockless_arm_sam.h"
+#include "clockless2.h"
+#include "block_clockless.h"
#endif
diff --git a/clockless2.h b/clockless2.h
index b137f6a8..68e6477a 100644
--- a/clockless2.h
+++ b/clockless2.h
@@ -6,7 +6,7 @@
#include "led_sysdefs.h"
#include "delay.h"
-
+#ifdef FASTLED_TEENSY3
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. The first point
@@ -104,7 +104,6 @@ public:
nLeds *= (3 + SKIP);
register uint8_t *end = data + nLeds;
-#if defined(FASTLED_TEENSY3)
register uint32_t b;
register uint32_t c;
if(ADVANCE) {
@@ -223,7 +222,6 @@ public:
c = scale8(c, scale);
delaycycles<T3 - 15>(); // 1/1 store, 2/2 load (with increment), 1/1 mul, 1/1 shift, 1 cmp, 1 branch backwards, 1 movim, 1/1 port lookup
};
-#endif
}
#ifdef SUPPORT_ARGB
@@ -238,4 +236,6 @@ public:
#undef SET2_LO
#undef SET2_LO2
+#endif
+
#endif \ No newline at end of file
diff --git a/clockless_arm_k20.h b/clockless_arm_k20.h
new file mode 100644
index 00000000..a0cf0998
--- /dev/null
+++ b/clockless_arm_k20.h
@@ -0,0 +1,139 @@
+#ifndef __INC_CLOCKLESS_ARM_K20_H
+#define __INC_CLOCKLESS_ARM_K20_H
+
+// Definition for a single channel clockless controller for the k20 family of chips, like that used in the teensy 3.0/3.1
+// See clockless.h for detailed info on how the template parameters are used.
+#if defined(FASTLED_TEENSY3)
+template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, bool FLIP = false, int WAIT_TIME = 50>
+class ClocklessController : public CLEDController {
+ typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
+ typedef typename FastPin<DATA_PIN>::port_t data_t;
+
+ data_t mPinMask;
+ data_ptr_t mPort;
+ CMinWait<WAIT_TIME> mWait;
+public:
+ virtual void init() {
+ FastPin<DATA_PIN>::setOutput();
+ mPinMask = FastPin<DATA_PIN>::mask();
+ mPort = FastPin<DATA_PIN>::port();
+ }
+
+ virtual void clearLeds(int nLeds) {
+ showColor(CRGB(0, 0, 0), nLeds, 0);
+ }
+
+ // set all the leds on the controller to a given color
+ virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
+ mWait.wait();
+ cli();
+
+ showRGBInternal<0, false>(nLeds, scale, (const byte*)&data);
+
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
+ MS_COUNTER += (microsTaken / 1000);
+ sei();
+ mWait.mark();
+ }
+
+ virtual void show(const struct CRGB *rgbdata, int nLeds, uint8_t scale = 255) {
+ mWait.wait();
+ cli();
+
+ showRGBInternal<0, true>(nLeds, scale, (const byte*)rgbdata);
+
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
+ MS_COUNTER += (microsTaken / 1000);
+ sei();
+ mWait.mark();
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void show(const struct CARGB *rgbdata, int nLeds, uint8_t scale = 255) {
+ mWait.wait();
+ cli();
+
+ showRGBInternal<1, true>(nLeds, scale, (const byte*)rgbdata);
+
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
+ MS_COUNTER += (microsTaken / 1000);
+ sei();
+ mWait.mark();
+ }
+#endif
+
+ inline static void write8Bits(register data_ptr_t port, register data_t hi, register data_t lo, register uint32_t & b) __attribute__ ((always_inline)) {
+ // TODO: hand rig asm version of this method. The timings are based on adjusting/studying GCC compiler ouptut. This
+ // will bite me in the ass at some point, I know it.
+ for(register uint32_t i = 7; i > 0; i--) {
+ FastPin<DATA_PIN>::fastset(port, hi);
+ delaycycles<T1 - 5>(); // 5 cycles - 2 store, 1 and, 1 test, 1 if
+ if(b & 0x80) { FastPin<DATA_PIN>::fastset(port, hi); } else { FastPin<DATA_PIN>::fastset(port, lo); }
+ b <<= 1;
+ delaycycles<T2 - 2>(); // 2 cycles, 1 store/skip, 1 shift
+ FastPin<DATA_PIN>::fastset(port, lo);
+ delaycycles<T3 - 5>(); // 3 cycles, 2 store, 1 sub, 1 branch backwards
+ }
+ // delay an extra cycle because falling out of the loop takes on less cycle than looping around
+ delaycycles<1>();
+
+ FastPin<DATA_PIN>::fastset(port, hi);
+ delaycycles<T1 - 6>();
+ if(b & 0x80) { FastPin<DATA_PIN>::fastset(port, hi); } else { FastPin<DATA_PIN>::fastset(port, lo); }
+ delaycycles<T2 - 2>(); // 4 cycles, 2 store, store/skip
+ FastPin<DATA_PIN>::fastset(port, lo);
+ }
+
+ // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+ // gcc will use register Y for the this pointer.
+ template<int SKIP, bool ADVANCE> static void showRGBInternal(register int nLeds, register uint8_t scale, register const byte *rgbdata) {
+ register byte *data = (byte*)rgbdata;
+ register data_t mask = FastPin<DATA_PIN>::mask();
+ register data_ptr_t port = FastPin<DATA_PIN>::port();
+ nLeds *= (3 + SKIP);
+ register uint8_t *end = data + nLeds;
+ register data_t hi = *port | mask;
+ register data_t lo = *port & ~mask;
+ *port = lo;
+
+ register uint32_t b;
+ b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE0(RGB_ORDER)];
+ b = scale8(b, scale);
+ while(data < end) {
+ // Write first byte, read next byte
+ write8Bits(port, hi, lo, b);
+
+ b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE1(RGB_ORDER)];
+ INLINE_SCALE(b, scale);
+ delaycycles<T3 - 5>(); // 1 store, 2 load, 1 mul, 1 shift,
+
+ // Write second byte
+ write8Bits(port, hi, lo, b);
+
+ b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE2(RGB_ORDER)];
+ INLINE_SCALE(b, scale);
+
+ data += 3 + SKIP;
+ if((RGB_ORDER & 0070) == 0) {
+ delaycycles<T3 - 6>(); // 1 store, 2 load, 1 mul, 1 shift, 1 adds if BRG or GRB
+ } else {
+ delaycycles<T3 - 5>(); // 1 store, 2 load, 1 mul, 1 shift,
+ }
+
+ // Write third byte
+ write8Bits(port, hi, lo, b);
+
+ b = ((ADVANCE)?data:rgbdata)[SKIP + RGB_BYTE0(RGB_ORDER)];
+ INLINE_SCALE(b, scale);
+
+ delaycycles<T3 - 11>(); // 1 store, 2 load (with increment), 1 mul, 1 shift, 1 cmp, 1 branch backwards, 1 movim
+ };
+ }
+};
+#endif
+
+#endif
+
diff --git a/clockless_arm_sam.h b/clockless_arm_sam.h
new file mode 100644
index 00000000..f8b1e0bd
--- /dev/null
+++ b/clockless_arm_sam.h
@@ -0,0 +1,213 @@
+#ifndef __INC_CLOCKLESS_ARM_SAM_H
+#define __INC_CLOCKLESS_ARM_SAM_H
+
+// Definition for a single channel clockless controller for the sam family of arm chips, like that used in the due and rfduino
+// See clockless.h for detailed info on how the template parameters are used.
+
+#if defined(__SAM3X8E__)
+
+template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, bool FLIP = false, int WAIT_TIME = 50>
+class ClocklessController : public CLEDController {
+ typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
+ typedef typename FastPinBB<DATA_PIN>::port_t data_t;
+
+ data_t mPinMask;
+ data_ptr_t mPort;
+ CMinWait<WAIT_TIME> mWait;
+public:
+ virtual void init() {
+ FastPinBB<DATA_PIN>::setOutput();
+ mPinMask = FastPinBB<DATA_PIN>::mask();
+ mPort = FastPinBB<DATA_PIN>::port();
+ }
+
+ virtual void clearLeds(int nLeds) {
+ showColor(CRGB(0, 0, 0), nLeds, 0);
+ }
+
+ // set all the leds on the controller to a given color
+ virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
+ mWait.wait();
+ cli();
+ SysClockSaver savedClock(T1 + T2 + T3);
+
+ showRGBInternal<0, false>(nLeds, scale, (const byte*)&data);
+
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
+ MS_COUNTER += (microsTaken / 1000);
+ savedClock.restore();
+ sei();
+ mWait.mark();
+ }
+
+ virtual void show(const struct CRGB *rgbdata, int nLeds, uint8_t scale = 255) {
+ mWait.wait();
+ cli();
+ SysClockSaver savedClock(T1 + T2 + T3);
+
+ // FastPinBB<DATA_PIN>::hi(); delay(1); FastPinBB<DATA_PIN>::lo();
+ showRGBInternal<0, true>(nLeds, scale, (const byte*)rgbdata);
+
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
+ MS_COUNTER += (microsTaken / 1000);
+ savedClock.restore();
+ sei();
+ mWait.mark();
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void show(const struct CARGB *rgbdata, int nLeds, uint8_t scale = 255) {
+ mWait.wait();
+ cli();
+ SysClockSaver savedClock(T1 + T2 + T3);
+
+ showRGBInternal<1, true>(nLeds, scale, (const byte*)rgbdata);
+
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
+ MS_COUNTER += (microsTaken / 1000);
+ savedClock.restore();
+ sei();
+ mWait.mark();
+ }
+#endif
+
+// I hate using defines for these, should find a better representation at some point
+#define _CTRL CTPTR[0]
+#define _LOAD CTPTR[1]
+#define _VAL CTPTR[2]
+
+ __attribute__((always_inline)) static inline void wait_loop_start(register volatile uint32_t *CTPTR) {
+ __asm__ __volatile__ (
+ "L_%=: ldr.w r8, [%0]\n"
+ " tst.w r8, #65536\n"
+ " beq.n L_%=\n"
+ : /* no outputs */
+ : "r" (CTPTR)
+ : "r8"
+ );
+ }
+
+ template<int MARK> __attribute__((always_inline)) static inline void wait_loop_mark(register volatile uint32_t *CTPTR) {
+ __asm__ __volatile__ (
+ "L_%=: ldr.w r8, [%0, #8]\n"
+ " cmp.w r8, %1\n"
+ " bhi.n L_%=\n"
+ : /* no outputs */
+ : "r" (CTPTR), "I" (MARK)
+ : "r8"
+ );
+ }
+
+ __attribute__((always_inline)) static inline void mark_port(register data_ptr_t port, register int val) {
+ __asm__ __volatile__ (
+ " str.w %0, [%1]\n"
+ : /* no outputs */
+ : "r" (val), "r" (port)
+ );
+ }
+#define AT_BIT_START(X) wait_loop_start(CTPTR); X;
+#define AT_MARK(X) wait_loop_mark<T1_MARK>(CTPTR); { X; }
+#define AT_END(X) wait_loop_mark<T2_MARK>(CTPTR); { X; }
+
+// #define AT_BIT_START(X) while(!(_CTRL & SysTick_CTRL_COUNTFLAG_Msk)); { X; }
+// #define AT_MARK(X) while(_VAL > T1_MARK); { X; }
+// #define AT_END(X) while(_VAL > T2_MARK); { X; }
+
+//#define AT_MARK(X) delayclocks_until<T1_MARK>(_VAL); X;
+//#define AT_END(X) delayclocks_until<T2_MARK>(_VAL); X;
+
+#define TOTAL (T1 + T2 + T3)
+
+#define T1_MARK (TOTAL - T1)
+#define T2_MARK (T1_MARK - T2)
+ template<int MARK> __attribute__((always_inline)) static inline void delayclocks_until(register byte b) {
+ __asm__ __volatile__ (
+ " sub %0, %0, %1\n"
+ "L_%=: subs %0, %0, #2\n"
+ " bcs.n L_%=\n"
+ : /* no outputs */
+ : "r" (b), "I" (MARK)
+ : /* no clobbers */
+ );
+
+ }
+
+#define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) )
+
+ // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+ // gcc will use register Y for the this pointer.
+ template<int SKIP, bool ADVANCE> static void showRGBInternal(register int nLeds, register uint8_t scale, register const byte *rgbdata) {
+ register data_ptr_t port asm("r7") = FastPinBB<DATA_PIN>::port(); FORCE_REFERENCE(port);
+ register byte *data = (byte*)rgbdata;
+ register uint8_t *end = data + (nLeds*3 + SKIP);
+
+ register volatile uint32_t *CTPTR asm("r6")= &SysTick->CTRL; FORCE_REFERENCE(CTPTR);
+
+ *port = 0;
+
+ register uint32_t b;
+ b = ADVANCE ? data[SKIP + RGB_BYTE0(RGB_ORDER)] :rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
+ b = scale8(b, scale);
+
+ // Setup and start the clock
+ _LOAD = TOTAL;
+ _VAL = 0;
+ _CTRL |= SysTick_CTRL_CLKSOURCE_Msk;
+ _CTRL |= SysTick_CTRL_ENABLE_Msk;
+
+ // read to clear the loop flag
+ _CTRL;
+
+ while(data < end) {
+ for(register uint32_t i = 7; i > 0; i--) {
+ AT_BIT_START(*port = 1);
+ if(b& 0x80) {} else { AT_MARK(*port = 0); }
+ AT_END(*port = 0);
+ b <<= 1;
+ }
+
+ AT_BIT_START(*port = 1);
+ if(b& 0x80) {} else { AT_MARK(*port = 0); }
+ AT_END(*port = 0);
+
+ b = ADVANCE ? data[SKIP + RGB_BYTE1(RGB_ORDER)] : rgbdata[SKIP + RGB_BYTE1(RGB_ORDER)];
+ b = scale8(b, scale);
+
+ for(register uint32_t i = 7; i > 0; i--) {
+ AT_BIT_START(*port = 1);
+ if(b& 0x80) {} else { AT_MARK(*port = 0); }
+ AT_END(*port = 0);
+ b <<= 1;
+ }
+
+ AT_BIT_START(*port = 1);
+ if(b& 0x80) {} else { AT_MARK(*port = 0); }
+ AT_END(*port = 0);
+
+ b = ADVANCE ? data[SKIP + RGB_BYTE2(RGB_ORDER)] : rgbdata[SKIP + RGB_BYTE2(RGB_ORDER)];
+ b = scale8(b, scale);
+ data += (3 + SKIP);
+
+ for(register uint32_t i = 7; i > 0; i--) {
+ AT_BIT_START(*port = 1);
+ if(b& 0x80) {} else { AT_MARK(*port = 0); }
+ AT_END(*port = 0);
+ b <<= 1;
+ }
+
+ AT_BIT_START(*port = 1);
+ if(b& 0x80) {} else { AT_MARK(*port = 0); }
+ AT_END(*port = 0);
+
+ b = ADVANCE ? data[SKIP + RGB_BYTE0(RGB_ORDER)] : rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
+ b = scale8(b, scale);
+ };
+ }
+};
+
+#endif
+
+#endif
diff --git a/clockless_avr.h b/clockless_avr.h
new file mode 100644
index 00000000..a71f23b2
--- /dev/null
+++ b/clockless_avr.h
@@ -0,0 +1,173 @@
+#ifndef __CLOCKLESS_AVR_H
+#define __CLOCKLESS_AVR_H
+
+#ifdef FASTLED_AVR
+
+// Definition for a single channel clockless controller for the avr family of chips, like those used in
+// the arduino and teensy 2.x. Note that there is a special case for hardware-mul-less versions of the avr,
+// which are tracked in clockless_trinket.h
+template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, bool FLIP = false, int WAIT_TIME = 50>
+class ClocklessController : public CLEDController {
+ typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
+ typedef typename FastPin<DATA_PIN>::port_t data_t;
+
+ data_t mPinMask;
+ data_ptr_t mPort;
+ CMinWait<WAIT_TIME> mWait;
+public:
+ virtual void init() {
+ FastPin<DATA_PIN>::setOutput();
+ mPinMask = FastPin<DATA_PIN>::mask();
+ mPort = FastPin<DATA_PIN>::port();
+ }
+
+ template <int N, int ADJ>inline static void bitSetLast(register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t b) {
+ // First cycle
+ SET_HI; // 1 clock cycle if using out, 2 otherwise
+ delaycycles<T1 - (_CYCLES(DATA_PIN) + 1)>(); // 1st cycle length minus 1 clock for out, 1 clock for sbrs
+ __asm__ __volatile__ ("sbrs %0, %1" :: "r" (b), "M" (N) :); // 1 clock for check (+1 if skipping, next op is also 1 clock)
+
+ // Second cycle
+ SET_LO; // 1/2 clock cycle if using out
+ delaycycles<T2 - _CYCLES(DATA_PIN)>(); // 2nd cycle length minus 1/2 clock for out
+
+ // Third cycle
+ SET_LO; // 1/2 clock cycle if using out
+ delaycycles<T3 - (_CYCLES(DATA_PIN) + ADJ)>(); // 3rd cycle length minus 7 clocks for out, loop compare, jump, next uint8_t load
+ }
+
+ virtual void clearLeds(int nLeds) {
+ showColor(CRGB(0, 0, 0), nLeds, 0);
+ }
+
+ // set all the leds on the controller to a given color
+ virtual void showColor(const struct CRGB & data, int nLeds, uint8_t scale = 255) {
+ mWait.wait();
+ cli();
+
+ showRGBInternal<0, false>(nLeds, scale, (const byte*)&data);
+
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
+ MS_COUNTER += (microsTaken / 1000);
+ sei();
+ mWait.mark();
+ }
+
+ virtual void show(const struct CRGB *rgbdata, int nLeds, uint8_t scale = 255) {
+ mWait.wait();
+ cli();
+
+ showRGBInternal<0, true>(nLeds, scale, (const byte*)rgbdata);
+
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
+ MS_COUNTER += (microsTaken / 1000);
+ sei();
+ mWait.mark();
+ }
+
+#ifdef SUPPORT_ARGB
+ virtual void show(const struct CARGB *rgbdata, int nLeds, uint8_t scale = 255) {
+ mWait.wait();
+ cli();
+
+ showRGBInternal<1, true>((long)nLeds, scale, (const byte*)rgbdata);
+
+ // Adjust the timer
+ long microsTaken = CLKS_TO_MICROS((long)nLeds * 8 * (T1 + T2 + T3));
+ MS_COUNTER += (microsTaken / 1000);
+ sei();
+ mWait.mark();
+ }
+#endif
+
+ // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
+ // gcc will use register Y for the this pointer.
+ template<int SKIP, bool ADVANCE> static void showRGBInternal(register int nLeds, register uint8_t scale, register const byte *rgbdata) {
+ register byte *data = (byte*)rgbdata;
+ register data_ptr_t port = FastPin<DATA_PIN>::port();
+ nLeds *= (3 + SKIP);
+ register uint8_t *end = data + nLeds;
+ register data_t hi = FastPin<DATA_PIN>::hival();
+ register data_t lo = FastPin<DATA_PIN>::loval();;
+ *port = lo;
+
+ register uint8_t b;
+
+ if(ADVANCE) {
+ b = data[SKIP + RGB_BYTE0(RGB_ORDER)];
+ } else {
+ b = rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
+ }
+ b = scale8_LEAVING_R1_DIRTY(b, scale);
+
+ register uint8_t c;
+ register uint8_t d;
+ while(data < end) {
+ for(register byte x=5; x; x--) {
+ bitSetLast<7, 4>(port, hi, lo, b);
+ b <<= 1;
+ }
+ delaycycles<1>();
+ // Leave an extra 2 clocks for the next byte load
+ bitSetLast<7, 1>(port, hi, lo, b);
+ delaycycles<1>();
+
+ // Leave an extra 4 clocks for the scale
+ bitSetLast<6, 6>(port, hi, lo, b);
+ if(ADVANCE) {
+ c = data[SKIP + RGB_BYTE1(RGB_ORDER)];
+ } else {
+ c = rgbdata[SKIP + RGB_BYTE1(RGB_ORDER)];
+ delaycycles<1>();
+ }
+ INLINE_SCALE(c, scale);
+ bitSetLast<5, 1>(port, hi, lo, b);
+
+ for(register byte x=5; x; x--) {
+ bitSetLast<7, 4>(port, hi, lo, c);
+ c <<= 1;
+ }
+ delaycycles<1>();
+ // Leave an extra 2 clocks for the next byte load
+ bitSetLast<7, 1>(port, hi, lo, c);
+ delaycycles<1>();
+
+ // Leave an extra 4 clocks for the scale
+ bitSetLast<6, 6>(port, hi, lo, c);
+ if(ADVANCE) {
+ d = data[SKIP + RGB_BYTE2(RGB_ORDER)];
+ } else {
+ d = rgbdata[SKIP + RGB_BYTE2(RGB_ORDER)];
+ delaycycles<1>();
+ }
+ INLINE_SCALE(d, scale);
+ bitSetLast<5, 1>(port, hi, lo, c);
+
+ for(register byte x=5; x; x--) {
+ bitSetLast<7, 4>(port, hi, lo, d);
+ d <<= 1;
+ }
+ delaycycles<1>();
+ // Leave an extra 2 clocks for the next byte load
+ bitSetLast<7, 2>(port, hi, lo, d);
+ data += (SKIP + 3);
+ // Leave an extra 4 clocks for the scale
+ bitSetLast<6, 6>(port, hi, lo, d);
+ if(ADVANCE) {
+ b = data[SKIP + RGB_BYTE0(RGB_ORDER)];
+ } else {
+ b = rgbdata[SKIP + RGB_BYTE0(RGB_ORDER)];
+ delaycycles<1>();
+ }
+ INLINE_SCALE(b, scale);
+ bitSetLast<5, 6>(port, hi, lo, d);
+ }
+ cleanup_R1();
+ }
+};
+#endif
+
+#endif
+
diff --git a/clockless_trinket.h b/clockless_trinket.h
index 168ba670..7fb593dd 100644
--- a/clockless_trinket.h
+++ b/clockless_trinket.h
@@ -6,6 +6,7 @@
#include "delay.h"
#include <avr/interrupt.h> // for cli/se definitions
+#if defined(LIB8_ATTINY)
// Scaling macro choice
#ifndef TRINKET_SCALE
@@ -277,3 +278,5 @@ public:
};
#endif
+
+#endif