diff options
Diffstat (limited to 'src')
22 files changed, 737 insertions, 195 deletions
diff --git a/src/FastLED.h b/src/FastLED.h index 042496fc..c6be2b5c 100644 --- a/src/FastLED.h +++ b/src/FastLED.h @@ -8,12 +8,14 @@ #define FASTLED_HAS_PRAGMA_MESSAGE #endif -#define FASTLED_VERSION 3004000 +#define FASTLED_VERSION 3005000 #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.) +# ifdef FASTLED_SHOW_VERSION +# ifdef FASTLED_HAS_PRAGMA_MESSAGE +# pragma message "FastLED version 3.005.000" +# else +# warning FastLED version 3.005.000 (Not really a warning, just telling you here.) +# endif # endif #endif diff --git a/src/controller.h b/src/controller.h index fe32d70d..7b7a7cf9 100644 --- a/src/controller.h +++ b/src/controller.h @@ -404,7 +404,11 @@ protected: ///@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()); + PixelController<RGB_ORDER, LANES, MASK> pixels(data, nLeds < 0 ? -nLeds : nLeds, scale, getDither()); + if(nLeds < 0) { + // nLeds < 0 implies that we want to show them in reverse + pixels.mAdvance = -pixels.mAdvance; + } showPixels(pixels); } diff --git a/src/fastled_delay.h b/src/fastled_delay.h index a14e8a29..4f99e3f6 100644 --- a/src/fastled_delay.h +++ b/src/fastled_delay.h @@ -8,6 +8,9 @@ FASTLED_NAMESPACE_BEGIN + +#if (!defined(NO_MINIMUM_WAIT) || (NO_MINIMUM_WAIT==0)) + /// 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 { @@ -26,6 +29,18 @@ public: void mark() { mLastMicros = micros() & 0xFFFF; } }; +#else + +// if you keep your own FPS (and therefore don't call show() too quickly for pixels to latch), you may not want a minimum wait. +template<int WAIT> class CMinWait { +public: + CMinWait() { } + void wait() { } + void mark() {} +}; + +#endif + //////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/src/fastspi.h b/src/fastspi.h index ae084f84..603e755a 100644 --- a/src/fastspi.h +++ b/src/fastspi.h @@ -13,7 +13,7 @@ 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) || (defined(ESP32) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)) +#elif defined(FASTLED_TEENSY4) || (defined(ESP32) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)) || (defined(ESP8266) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)) // just use clocks #define DATA_RATE_MHZ(X) (1000000 * (X)) #define DATA_RATE_KHZ(X) (1000 * (X)) @@ -59,6 +59,11 @@ template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class SPIOutput : public ESP32SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; #endif +#if defined(ESP8266) && defined(FASTLED_ALL_PINS_HARDWARE_SPI) +template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> +class SPIOutput : public ESP8266SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; +#endif + #if defined(SPI_DATA) && defined(SPI_CLOCK) #if defined(FASTLED_TEENSY3) && defined(ARM_HARDWARE_SPI) @@ -81,13 +86,13 @@ class SPIOutput<SPI2_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<S #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> {}; +class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public Teensy4HardwareSPIOutput<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> {}; +class SPIOutput<SPI1_DATA, SPI_CLOCK, SPI_SPEED> : public Teensy4HardwareSPIOutput<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> {}; +class SPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED> : public Teensy4HardwareSPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED, SPI2, 2> {}; #elif defined(FASTLED_TEENSYLC) && defined(ARM_HARDWARE_SPI) diff --git a/src/lib8tion.h b/src/lib8tion.h index 0cc3baa4..43dedbc0 100644 --- a/src/lib8tion.h +++ b/src/lib8tion.h @@ -33,7 +33,7 @@ FASTLED_NAMESPACE_BEGIN qsub8( i, j) == MAX( (i - j), 0 ) - Saturating signed 8-bit ("7-bit") add. - qadd7( i, j) == MIN( (i + j), 0x7F) + qadd7( i, j) == MAX( MIN( (i + j), 0x7F), -0x80) - Scaling (down) of unsigned 8- and 16- bit values. @@ -99,7 +99,7 @@ FASTLED_NAMESPACE_BEGIN - Fast 8-bit "easing in/out" function. - ease8InOutCubic(x) == 3(x^i) - 2(x^3) + ease8InOutCubic(x) == 3(x^2) - 2(x^3) ease8InOutApprox(x) == faster, rougher, approximation of cubic easing ease8InOutQuad(x) == quadratic (vs cubic) easing @@ -208,8 +208,10 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun #define SUB8_C 1 #define EASE8_C 1 #define AVG8_C 1 +#define AVG8R_C 1 #define AVG7_C 1 #define AVG16_C 1 +#define AVG16R_C 1 #define AVG15_C 1 #define BLEND8_C 1 @@ -231,8 +233,10 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun #define SUB8_C 1 #define EASE8_C 1 #define AVG8_C 1 +#define AVG8R_C 1 #define AVG7_C 1 #define AVG16_C 1 +#define AVG16R_C 1 #define AVG15_C 1 #define BLEND8_C 1 @@ -249,8 +253,10 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun #define ADD8_C 0 #define SUB8_C 0 #define AVG8_C 0 +#define AVG8R_C 0 #define AVG7_C 0 #define AVG16_C 0 +#define AVG16R_C 0 #define AVG15_C 0 #define QADD8_AVRASM 1 @@ -260,8 +266,10 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun #define ADD8_AVRASM 1 #define SUB8_AVRASM 1 #define AVG8_AVRASM 1 +#define AVG8R_AVRASM 1 #define AVG7_AVRASM 1 #define AVG16_AVRASM 1 +#define AVG16R_AVRASM 1 #define AVG15_AVRASM 1 // Note: these require hardware MUL instruction @@ -319,8 +327,10 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun #define SUB8_C 1 #define EASE8_C 1 #define AVG8_C 1 +#define AVG8R_C 1 #define AVG7_C 1 #define AVG16_C 1 +#define AVG16R_C 1 #define AVG15_C 1 #define BLEND8_C 1 @@ -732,9 +742,9 @@ LIB8STATIC uint8_t ease8InOutApprox( fract8 i) "Ldone_%=: \n\t" - : [i] "+&a" (i) + : [i] "+a" (i) : - : "r0", "r1" + : "r0" ); return i; } @@ -744,7 +754,7 @@ LIB8STATIC uint8_t ease8InOutApprox( fract8 i) -/// triwave8: triangle (sawtooth) wave generator. Useful for +/// triwave8: triangle wave generator. Useful for /// turning a one-byte ever-increasing value into a /// one-byte value that oscillates up and down. /// @@ -834,10 +844,7 @@ public: uint16_t operator*(uint16_t v) { return (v*i) + ((v*f)>>F); } int32_t operator*(int32_t v) { return (v*i) + ((v*f)>>F); } int16_t operator*(int16_t v) { return (v*i) + ((v*f)>>F); } -#ifdef FASTLED_ARM - int operator*(int v) { return (v*i) + ((v*f)>>F); } -#endif -#ifdef FASTLED_APOLLO3 +#if defined(FASTLED_ARM) | defined(FASTLED_RISCV) | defined(FASTLED_APOLLO3) int operator*(int v) { return (v*i) + ((v*f)>>F); } #endif }; @@ -846,10 +853,7 @@ template<class T, int F, int I> static uint32_t operator*(uint32_t v, q<T,F,I> & template<class T, int F, int I> static uint16_t operator*(uint16_t v, q<T,F,I> & q) { return q * v; } template<class T, int F, int I> static int32_t operator*(int32_t v, q<T,F,I> & q) { return q * v; } template<class T, int F, int I> static int16_t operator*(int16_t v, q<T,F,I> & q) { return q * v; } -#ifdef FASTLED_ARM -template<class T, int F, int I> static int operator*(int v, q<T,F,I> & q) { return q * v; } -#endif -#ifdef FASTLED_APOLLO3 +#if defined(FASTLED_ARM) | defined(FASTLED_RISCV) | defined(FASTLED_APOLLO3) template<class T, int F, int I> static int operator*(int v, q<T,F,I> & q) { return q * v; } #endif diff --git a/src/lib8tion/math8.h b/src/lib8tion/math8.h index a83b1ad2..19a5ad77 100644 --- a/src/lib8tion/math8.h +++ b/src/lib8tion/math8.h @@ -50,27 +50,32 @@ LIB8STATIC_ALWAYS_INLINE uint8_t qadd8( uint8_t i, uint8_t j) #endif } -/// Add one byte to another, saturating at 0x7F +/// Add one byte to another, saturating at 0x7F and -0x80 /// @param i - first byte to add /// @param j - second byte to add -/// @returns the sum of i & j, capped at 0xFF +/// @returns the sum of i & j, capped at 0x7F and -0x80 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; + else if( t < -128) t = -128; return t; #elif QADD7_AVRASM == 1 asm volatile( - /* First, add j to i, conditioning the V flag */ + /* First, add j to i, conditioning the V and C flags */ "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 clear, we branch to end. If V is set, we go ahead and load 0x7F into i. */ "brvc L_%= \n\t" "ldi %0, 0x7F \n\t" + + /* When both numbers are negative, C is set. + Adding it to make result negative. */ + "adc %0, __zero_reg__\n\t" "L_%=: " : "+a" (i) : "a" (j) @@ -129,7 +134,7 @@ LIB8STATIC_ALWAYS_INLINE uint8_t add8( uint8_t i, uint8_t j) #endif } -/// add one byte to another, with one byte result +/// add one byte to two bytes, with two bytes result LIB8STATIC_ALWAYS_INLINE uint16_t add8to16( uint8_t i, uint16_t j) { #if ADD8_C == 1 @@ -213,15 +218,68 @@ LIB8STATIC_ALWAYS_INLINE uint16_t avg16( uint16_t i, uint16_t j) #endif } +/// Calculate an integer average of two unsigned +/// 8-bit integer values (uint8_t). +/// Fractional results are rounded up, e.g. avg8r(20,41) = 31 +LIB8STATIC_ALWAYS_INLINE uint8_t avg8r( uint8_t i, uint8_t j) +{ +#if AVG8R_C == 1 + return (i + j + 1) >> 1; +#elif AVG8R_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, old 1st bit now in C */ + "ror %0 \n\t" + /* Add C flag */ + "adc %0, __zero_reg__\n\t" + : "+a" (i) + : "a" (j) + ); + return i; +#else +#error "No implementation for avg8r available." +#endif +} + +/// Calculate an integer average of two unsigned +/// 16-bit integer values (uint16_t). +/// Fractional results are rounded up, e.g. avg16r(20,41) = 31 +LIB8STATIC_ALWAYS_INLINE uint16_t avg16r( uint16_t i, uint16_t j) +{ +#if AVG16R_C == 1 + return (uint32_t)((uint32_t)(i) + (uint32_t)(j) + 1) >> 1; +#elif AVG16R_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, old 1st bit now in C */ + "ror %A[i] \n\t" + /* Add C flag */ + "adc %A[i], __zero_reg__\n\t" + "adc %B[i], __zero_reg__\n\t" + : [i] "+a" (i) + : [j] "a" (j) + ); + return i; +#else +#error "No implementation for avg16r 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. +/// If the first argument is odd, result is rounded up. LIB8STATIC_ALWAYS_INLINE int8_t avg7( int8_t i, int8_t j) { #if AVG7_C == 1 - return ((i + j) >> 1) + (i & 0x1); + return (i>>1) + (j>>1) + (i & 0x1); #elif AVG7_AVRASM == 1 asm volatile( "asr %1 \n\t" @@ -239,11 +297,11 @@ LIB8STATIC_ALWAYS_INLINE int8_t avg7( int8_t i, int8_t j) /// 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. +/// If the first argument is odd, result is rounded 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); + return (i>>1) + (j>>1) + (i & 0x1); #elif AVG15_AVRASM == 1 asm volatile( /* first divide j by 2, throwing away lowest bit */ @@ -321,13 +379,6 @@ LIB8STATIC uint8_t addmod8( uint8_t a, uint8_t b, uint8_t m) /// 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) { @@ -376,23 +427,21 @@ LIB8STATIC_ALWAYS_INLINE uint8_t mul8( uint8_t i, uint8_t j) LIB8STATIC_ALWAYS_INLINE uint8_t qmul8( uint8_t i, uint8_t j) { #if QMUL8_C == 1 - int p = ((int)i * (int)(j) ); + unsigned p = (unsigned)i * (unsigned)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" + /* Extract the LOW 8-bits (r0) */ + " mov %0, r0 \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" + /* If high byte of result > 0, saturate to 0xFF */ + " ldi %0, 0xFF \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) @@ -461,7 +510,7 @@ LIB8STATIC uint8_t sqrt16(uint16_t x) return low - 1; } -/// blend a variable proproportion(0-255) of one byte to another +/// blend a variable proportion(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 @@ -469,32 +518,77 @@ LIB8STATIC uint8_t sqrt16(uint16_t x) #if (FASTLED_BLEND_FIXED == 1) LIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB) { -#if BLEND8_C == 1 + + // The BLEND_FIXED formula is + // + // result = ( A*(amountOfA) + B*(amountOfB) )/ 256 + // + // …where amountOfA = 255-amountOfB. + // + // This formula will never return 255, which is why the BLEND_FIXED + SCALE8_FIXED version is + // + // result = ( A*(amountOfA) + A + B*(amountOfB) + B ) / 256 + // + // We can rearrange this formula for some great optimisations. + // + // result = ( A*(amountOfA) + A + B*(amountOfB) + B ) / 256 + // = ( A*(255-amountOfB) + A + B*(amountOfB) + B ) / 256 + // = ( A*(256-amountOfB) + B*(amountOfB) + B ) / 256 + // = ( A*256 + B + B*(amountOfB) - A*(amountOfB) ) / 256 // this is the version used in SCALE8_FIXED AVR below + // = ( A*256 + B + (B-A)*(amountOfB) ) / 256 // this is the version used in SCALE8_FIXED C below + uint16_t partial; uint8_t result; - + +#if BLEND8_C == 1 + +# if (FASTLED_SCALE8_FIXED == 1) + partial = (a << 8) | b; // A*256 + B + + // on many platforms this compiles to a single multiply of (B-A) * amountOfB + partial += (b * amountOfB); + partial -= (a * amountOfB); + +# else uint8_t amountOfA = 255 - amountOfB; - + + // on the other hand, this compiles to two multiplies, and gives the "wrong" answer :] 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 +# endif result = partial >> 8; return result; #elif BLEND8_AVRASM == 1 - uint16_t partial; - uint8_t result; +# if (FASTLED_SCALE8_FIXED == 1) + + // 1 or 2 cycles depending on how the compiler optimises + partial = (a << 8) | b; + + // 7 cycles + asm volatile ( + " mul %[a], %[amountOfB] \n\t" + " sub %A[partial], r0 \n\t" + " sbc %B[partial], r1 \n\t" + " mul %[b], %[amountOfB] \n\t" + " add %A[partial], r0 \n\t" + " adc %B[partial], r1 \n\t" + " clr __zero_reg__ \n\t" + : [partial] "+r" (partial) + : [amountOfB] "r" (amountOfB), + [a] "r" (a), + [b] "r" (b) + : "r0", "r1" + ); + +# else + + // non-SCALE8-fixed version + + // 7 cycles asm volatile ( /* partial = b * amountOfB */ " mul %[b], %[amountOfB] \n\t" @@ -510,30 +604,22 @@ LIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB) " 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" ); + +# endif result = partial >> 8; return result; #else -#error "No implementation for blend8 available." +# error "No implementation for blend8 available." #endif } diff --git a/src/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h b/src/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h index ed3be816..1dd0021d 100644 --- a/src/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h +++ b/src/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h @@ -56,24 +56,24 @@ protected: next_mark = ARM_DWT_CYCCNT + off[0]; FastPin<DATA_PIN>::hi(); if(b&0x80) { - while((next_mark - ARM_DWT_CYCCNT) > off[1]); + while((next_mark - ARM_DWT_CYCCNT) > off[2]); FastPin<DATA_PIN>::lo(); } else { - while((next_mark - ARM_DWT_CYCCNT) > off[2]); + while((next_mark - ARM_DWT_CYCCNT) > off[1]); FastPin<DATA_PIN>::lo(); } b <<= 1; } while(ARM_DWT_CYCCNT < next_mark); - next_mark = ARM_DWT_CYCCNT + off[1]; + next_mark = ARM_DWT_CYCCNT + off[0]; 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]); + while((next_mark - ARM_DWT_CYCCNT) > off[1]); FastPin<DATA_PIN>::lo(); } } @@ -91,7 +91,7 @@ protected: 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 wait_off = _FASTLED_NS_TO_DWT((WAIT_TIME-INTERRUPT_THRESHOLD)*1000); uint32_t next_mark = ARM_DWT_CYCCNT + off[0]; diff --git a/src/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h b/src/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h index 5098af33..0cd53602 100644 --- a/src/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h +++ b/src/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h @@ -3,7 +3,7 @@ #include "fastpin_arm_mxrt1062.h" #include "fastspi_arm_mxrt1062.h" -#include "../k20/octows2811_controller.h" +#include "octows2811_controller.h" #include "../k20/ws2812serial_controller.h" #include "../k20/smartmatrix_t3.h" #include "clockless_arm_mxrt1062.h" diff --git a/src/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h b/src/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h index 068c7be1..6a9f514d 100644 --- a/src/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h +++ b/src/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h @@ -7,7 +7,7 @@ FASTLED_NAMESPACE_BEGIN #include <SPI.h> template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_RATE, SPIClass & _SPIObject, int _SPI_INDEX> -class Teesy4HardwareSPIOutput { +class Teensy4HardwareSPIOutput { Selectable *m_pSelect; uint32_t m_bitCount; uint32_t m_bitData; @@ -23,8 +23,8 @@ class Teesy4HardwareSPIOutput { } public: - Teesy4HardwareSPIOutput() { m_pSelect = NULL; m_bitCount = 0;} - Teesy4HardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; m_bitCount = 0;} + Teensy4HardwareSPIOutput() { m_pSelect = NULL; m_bitCount = 0;} + Teensy4HardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; m_bitCount = 0;} // set the object representing the selectable -- ignore for now void setSelect(Selectable *pSelect) { /* TODO */ } diff --git a/src/platforms/arm/mxrt1062/octows2811_controller.h b/src/platforms/arm/mxrt1062/octows2811_controller.h new file mode 100644 index 00000000..a7c2f3e4 --- /dev/null +++ b/src/platforms/arm/mxrt1062/octows2811_controller.h @@ -0,0 +1,64 @@ +#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 */ } + + virtual void showPixels(PixelController<RGB_ORDER, 8, 0xFF> &pixels) { + uint32_t size = pixels.size(); + uint32_t sizeTimes8 = 8U * size; + _init(size); + + uint32_t index = 0; + while (pixels.has(1)) { + for (int lane = 0; lane < 8; lane++) { + uint8_t r = pixels.loadAndScale0(lane); + uint8_t g = pixels.loadAndScale1(lane); + uint8_t b = pixels.loadAndScale2(lane); + pocto->setPixel(index, r, g, b); + index += size; + } + index -= sizeTimes8; + index++; + pixels.stepDithering(); + pixels.advanceData(); + } + + pocto->show(); + } + +}; + +FASTLED_NAMESPACE_END + +#endif + +#endif diff --git a/src/platforms/avr/clockless_trinket.h b/src/platforms/avr/clockless_trinket.h index 2cfbef0d..176221d9 100644 --- a/src/platforms/avr/clockless_trinket.h +++ b/src/platforms/avr/clockless_trinket.h @@ -87,7 +87,14 @@ template<> __attribute__((always_inline)) inline void _dc<20>(register uint8_t & // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#if (!defined(NO_CORRECTION) || (NO_CORRECTION == 0)) && (FASTLED_ALLOW_INTERRUPTS == 0) +#if ((FASTLED_ALLOW_INTERRUPTS == 0) && defined(NO_CORRECTION) && (NO_CORRECTION == 1) && !(defined(NO_CLOCK_CORRECTION))) +// we hit this if you were trying to turn off clock correction without also trying to enable interrupts. +# pragma message "In older versions of FastLED defining NO_CORRECTION 1 would mistakenly turn off color correction as well as clock correction." +# pragma message "define NO_CLOCK_CORRECTION 1 to fix this warning." +# define NO_CLOCK_CORRECTION 1 +#endif + +#if (!defined(NO_CLOCK_CORRECTION) || (NO_CLOCK_CORRECTION == 0)) && (FASTLED_ALLOW_INTERRUPTS == 0) static uint8_t gTimeErrorAccum256ths; #endif @@ -115,10 +122,12 @@ protected: mWait.wait(); cli(); - showRGBInternal(pixels); + if(pixels.mLen > 0) { + showRGBInternal(pixels); + } // Adjust the timer -#if (!defined(NO_CORRECTION) || (NO_CORRECTION == 0)) && (FASTLED_ALLOW_INTERRUPTS == 0) +#if (!defined(NO_CLOCK_CORRECTION) || (NO_CLOCK_CORRECTION == 0)) && (FASTLED_ALLOW_INTERRUPTS == 0) uint32_t microsTaken = (uint32_t)pixels.size() * (uint32_t)CLKS_TO_MICROS(24 * (T1 + T2 + T3)); // adust for approximate observed actal runtime (as of January 2015) @@ -241,13 +250,13 @@ protected: #define PRESCALEB4(D) asm __volatile__("brcc L_%=\n\tldi %[scale_base], 0xFF\n\tL_%=:\n\tneg %[" #D "]\n\tCLC" ASM_VARS); // Clamp for prescale, increment data, since we won't ever wrap 65k, this also effectively clears carry for us -#define PSBIDATA4(D) asm __volatile__("brcc L_%=\n\tldi %[scale_base], 0xFF\n\tL_%=:\n\tadd %A[data], %[ADV]\n\tadc %B[data], __zero_reg__\n\t" ASM_VARS); +#define PSBIDATA4(D) asm __volatile__("brcc L_%=\n\tldi %[scale_base], 0xFF\n\tL_%=:\n\tadd %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]\n\t" ASM_VARS); #else #define PRESCALE4(D) _dc<4>(loopvar); #define PRESCALEA2(D) _dc<2>(loopvar); #define PRESCALEB4(D) _dc<4>(loopvar); -#define PSBIDATA4(D) asm __volatile__( "add %A[data], %[ADV]\n\tadc %B[data], __zero_reg__\n\trjmp .+0\n\t" ASM_VARS ); +#define PSBIDATA4(D) asm __volatile__( "add %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]\n\trjmp .+0\n\t" ASM_VARS ); #endif // 2 cycles - perform one step of the scaling (if a given bit is set in scale, add scale-base to the scratch space) @@ -299,8 +308,8 @@ protected: #define DONE asm __volatile__("2:" ASM_VARS ); // 2 cycles - increment the data pointer -#define IDATA2 asm __volatile__("add %A[data], %[ADV]\n\tadc %B[data], __zero_reg__\n\t" ASM_VARS ); -#define IDATACLC3 asm __volatile__("add %A[data], %[ADV]\n\tadc %B[data], __zero_reg__\n\t" _CLC1 ASM_VARS ); +#define IDATA2 asm __volatile__("add %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]\n\t" ASM_VARS ); +#define IDATACLC3 asm __volatile__("add %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]\n\t" _CLC1 ASM_VARS ); // 1 cycle mov #define _MOV1(B1, B2) "mov %[" #B1 "], %[" #B2 "]\n\t" @@ -368,7 +377,9 @@ protected: pixels.preStepFirstByteDithering(); // pull the dithering/adjustment values out of the pixels object for direct asm access - uint8_t advanceBy = pixels.advanceBy(); + + // even though advanceBy is only an int8, we cast it to int16 for sign extension in case it's negative. + int16_t advanceBy = pixels.advanceBy(); uint16_t count = pixels.mLen; uint8_t s0 = pixels.mScale.raw[RO(0)]; @@ -473,10 +484,6 @@ protected: DONE; } - #if (FASTLED_ALLOW_INTERRUPTS == 1) - // stop using the clock juggler - TCCR0A &= ~0x30; - #endif } }; diff --git a/src/platforms/esp/32/clockless_block_esp32.h b/src/platforms/esp/32/clockless_block_esp32.h index 45b7671c..3e3c139e 100644 --- a/src/platforms/esp/32/clockless_block_esp32.h +++ b/src/platforms/esp/32/clockless_block_esp32.h @@ -21,7 +21,10 @@ class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LAN typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t; typedef typename FastPin<FIRST_PIN>::port_t data_t; - data_t mPinMask; + // Verify that the pin is valid + static_assert(FastPin<FIRST_PIN>::validpin(), "Invalid pin specified"); + + data_t mPinMask; data_ptr_t mPort; CMinWait<WAIT_TIME> mWait; diff --git a/src/platforms/esp/32/clockless_i2s_esp32.h b/src/platforms/esp/32/clockless_i2s_esp32.h index 6306ccd2..845d625b 100644 --- a/src/platforms/esp/32/clockless_i2s_esp32.h +++ b/src/platforms/esp/32/clockless_i2s_esp32.h @@ -201,8 +201,8 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER> // -- Store the GPIO pin gpio_num_t mPin; - // -- This instantiation forces a check on the pin choice - FastPin<DATA_PIN> mFastPin; + // -- Verify that the pin is valid + static_assert(FastPin<DATA_PIN>::validpin(), "Invalid pin specified"); // -- Save the pixel controller PixelController<RGB_ORDER> * mPixels; diff --git a/src/platforms/esp/32/clockless_rmt_esp32.cpp b/src/platforms/esp/32/clockless_rmt_esp32.cpp index 90ca046f..0f8ad9b3 100644 --- a/src/platforms/esp/32/clockless_rmt_esp32.cpp +++ b/src/platforms/esp/32/clockless_rmt_esp32.cpp @@ -108,12 +108,14 @@ uint8_t * ESP32RMTController::getPixelBuffer(int size_in_bytes) void ESP32RMTController::init(gpio_num_t pin) { if (gInitialized) return; + esp_err_t espErr = ESP_OK; for (int i = 0; i < gMaxChannel; i += gMemBlocks) { gOnChannel[i] = NULL; // -- RMT configuration for transmission rmt_config_t rmt_tx; + memset(&rmt_tx, 0, sizeof(rmt_config_t)); rmt_tx.channel = rmt_channel_t(i); rmt_tx.rmt_mode = RMT_MODE_TX; rmt_tx.gpio_num = pin; @@ -126,7 +128,8 @@ void ESP32RMTController::init(gpio_num_t pin) rmt_tx.tx_config.idle_output_en = true; // -- Apply the configuration - rmt_config(&rmt_tx); + espErr = rmt_config(&rmt_tx); + FASTLED_DEBUG("rmt_config result: %d", espErr); if (FASTLED_RMT_BUILTIN_DRIVER) { rmt_driver_install(rmt_channel_t(i), 0, 0); @@ -134,7 +137,8 @@ void ESP32RMTController::init(gpio_num_t pin) // -- Set up the RMT to send 32 bits of the pulse buffer and then // generate an interrupt. When we get this interrupt we // fill the other part in preparation (like double-buffering) - rmt_set_tx_thr_intr_en(rmt_channel_t(i), true, PULSES_PER_FILL); + espErr = rmt_set_tx_thr_intr_en(rmt_channel_t(i), true, PULSES_PER_FILL); + FASTLED_DEBUG("rmt_set_tx_thr_intr_en result: %d", espErr); } } @@ -232,6 +236,7 @@ void IRAM_ATTR ESP32RMTController::startNext(int channel) // for it to finish. void IRAM_ATTR ESP32RMTController::startOnChannel(int channel) { + esp_err_t espErr = ESP_OK; // -- Assign this channel and configure the RMT mRMT_channel = rmt_channel_t(channel); @@ -240,7 +245,13 @@ void IRAM_ATTR ESP32RMTController::startOnChannel(int channel) gOnChannel[channel] = this; // -- Assign the pin to this channel - rmt_set_pin(mRMT_channel, RMT_MODE_TX, mPin); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) + espErr = rmt_set_gpio(mRMT_channel, RMT_MODE_TX, mPin, false); + FASTLED_DEBUG("rmt_set_gpio result: %d", espErr); +#else + espErr = rmt_set_pin(mRMT_channel, RMT_MODE_TX, mPin); + FASTLED_DEBUG("rrmt_set_pin result: %d", espErr); +#endif if (FASTLED_RMT_BUILTIN_DRIVER) { // -- Use the built-in RMT driver to send all the data in one shot @@ -262,7 +273,8 @@ void IRAM_ATTR ESP32RMTController::startOnChannel(int channel) fillNext(false); // -- Turn on the interrupts - rmt_set_tx_intr_en(mRMT_channel, true); + espErr = rmt_set_tx_intr_en(mRMT_channel, true); + FASTLED_DEBUG("rmt_set_tx_intr_en result: %d", espErr); // -- Kick off the transmission tx_start(); @@ -275,11 +287,46 @@ void IRAM_ATTR ESP32RMTController::tx_start() { // rmt_tx_start(mRMT_channel, true); // Inline the code for rmt_tx_start, so it can be placed in IRAM +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 + // rmt_ll_tx_reset_pointer(&RMT, mRMT_channel) + RMT.tx_conf[mRMT_channel].mem_rd_rst = 1; + RMT.tx_conf[mRMT_channel].mem_rd_rst = 0; + RMT.tx_conf[mRMT_channel].mem_rst = 1; + RMT.tx_conf[mRMT_channel].mem_rst = 0; + // rmt_ll_clear_tx_end_interrupt(&RMT, mRMT_channel) + RMT.int_clr.val = (1 << (mRMT_channel)); + // rmt_ll_enable_tx_end_interrupt(&RMT, mRMT_channel, true) + RMT.int_ena.val |= (1 << mRMT_channel); + // rmt_ll_tx_start(&RMT, mRMT_channel) + RMT.tx_conf[mRMT_channel].conf_update = 1; + RMT.tx_conf[mRMT_channel].tx_start = 1; +#elif CONFIG_IDF_TARGET_ESP32S3 + // rmt_ll_tx_reset_pointer(&RMT, mRMT_channel) + RMT.chnconf0[mRMT_channel].mem_rd_rst_n = 1; + RMT.chnconf0[mRMT_channel].mem_rd_rst_n = 0; + RMT.chnconf0[mRMT_channel].apb_mem_rst_n = 1; + RMT.chnconf0[mRMT_channel].apb_mem_rst_n = 0; + // rmt_ll_clear_tx_end_interrupt(&RMT, mRMT_channel) + RMT.int_clr.val = (1 << (mRMT_channel)); + // rmt_ll_enable_tx_end_interrupt(&RMT, mRMT_channel, true) + RMT.int_ena.val |= (1 << mRMT_channel); + // rmt_ll_tx_start(&RMT, mRMT_channel) + RMT.chnconf0[mRMT_channel].conf_update_n = 1; + RMT.chnconf0[mRMT_channel].tx_start_n = 1; +#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 + // rmt_ll_tx_reset_pointer(&RMT, mRMT_channel) RMT.conf_ch[mRMT_channel].conf1.mem_rd_rst = 1; RMT.conf_ch[mRMT_channel].conf1.mem_rd_rst = 0; + // rmt_ll_clear_tx_end_interrupt(&RMT, mRMT_channel) + RMT.int_clr.val = (1 << (mRMT_channel * 3)); + // rmt_ll_enable_tx_end_interrupt(&RMT, mRMT_channel, true) RMT.int_ena.val &= ~(1 << (mRMT_channel * 3)); RMT.int_ena.val |= (1 << (mRMT_channel * 3)); + // rmt_ll_tx_start(&RMT, mRMT_channel) RMT.conf_ch[mRMT_channel].conf1.tx_start = 1; +#else + #error Not yet implemented for unknown ESP32 target +#endif mLastFill = __clock_cycles(); } @@ -299,10 +346,51 @@ void IRAM_ATTR ESP32RMTController::doneOnChannel(rmt_channel_t channel, void * a // -- Turn off the interrupts // rmt_set_tx_intr_en(channel, false); - // Inline the code for rmt_tx_stop, so it can be placed in IRAM + + // Inline the code for rmt_set_tx_intr_en(channel, false) and rmt_tx_stop, so it can be placed in IRAM +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 + // rmt_ll_enable_tx_end_interrupt(&RMT, channel) + RMT.int_ena.val &= ~(1 << channel); + // rmt_ll_tx_stop(&RMT, channel) + RMT.tx_conf[channel].tx_stop = 1; + RMT.tx_conf[channel].conf_update = 1; + // rmt_ll_tx_reset_pointer(&RMT, channel) + RMT.tx_conf[channel].mem_rd_rst = 1; + RMT.tx_conf[channel].mem_rd_rst = 0; + RMT.tx_conf[channel].mem_rst = 1; + RMT.tx_conf[channel].mem_rst = 0; +#elif CONFIG_IDF_TARGET_ESP32S3 + // rmt_ll_enable_tx_end_interrupt(&RMT, channel) + RMT.int_ena.val &= ~(1 << channel); + // rmt_ll_tx_stop(&RMT, channel) + RMT.chnconf0[channel].tx_stop_n = 1; + RMT.chnconf0[channel].conf_update_n = 1; + // rmt_ll_tx_reset_pointer(&RMT, channel) + RMT.chnconf0[channel].mem_rd_rst_n = 1; + RMT.chnconf0[channel].mem_rd_rst_n = 0; + RMT.chnconf0[channel].apb_mem_rst_n = 1; + RMT.chnconf0[channel].apb_mem_rst_n = 0; +#elif CONFIG_IDF_TARGET_ESP32S2 + // rmt_ll_enable_tx_end_interrupt(&RMT, channel) + RMT.int_ena.val &= ~(1 << (channel * 3)); + // rmt_ll_tx_stop(&RMT, channel) + RMT.conf_ch[channel].conf1.tx_stop = 1; + // rmt_ll_tx_reset_pointer(&RMT, channel) + RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + RMT.conf_ch[channel].conf1.mem_rd_rst = 0; +#elif CONFIG_IDF_TARGET_ESP32 + // rmt_ll_enable_tx_end_interrupt(&RMT, channel) RMT.int_ena.val &= ~(1 << (channel * 3)); + // rmt_ll_tx_stop(&RMT, channel) + RMT.conf_ch[channel].conf1.tx_start = 0; RMT.conf_ch[channel].conf1.mem_rd_rst = 1; RMT.conf_ch[channel].conf1.mem_rd_rst = 0; + // rmt_ll_tx_reset_pointer(&RMT, channel) + // RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + // RMT.conf_ch[channel].conf1.mem_rd_rst = 0; +#else + #error Not yet implemented for unknown ESP32 target +#endif gOnChannel[channel] = NULL; gNumDone++; @@ -337,11 +425,17 @@ void IRAM_ATTR ESP32RMTController::interruptHandler(void *arg) uint8_t channel; for (channel = 0; channel < gMaxChannel; channel++) { + #if CONFIG_IDF_TARGET_ESP32S2 int tx_done_bit = channel * 3; - #ifdef CONFIG_IDF_TARGET_ESP32S2 int tx_next_bit = channel + 12; - #else + #elif CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 + int tx_done_bit = channel; + int tx_next_bit = channel + 8; + #elif CONFIG_IDF_TARGET_ESP32 + int tx_done_bit = channel * 3; int tx_next_bit = channel + 24; + #else + #error Not yet implemented for unknown ESP32 target #endif ESP32RMTController * pController = gOnChannel[channel]; @@ -369,9 +463,9 @@ void IRAM_ATTR ESP32RMTController::fillNext(bool check_time) { uint32_t now = __clock_cycles(); if (check_time) { - if (mLastFill != 0 and now > mLastFill) { - uint32_t delta = (now - mLastFill); - if (delta > mMaxCyclesPerFill) { + if (mLastFill != 0) { + int32_t delta = (now - mLastFill); + if (delta > (int32_t)mMaxCyclesPerFill) { // Serial.print(delta); // Serial.print(" BAIL "); // Serial.println(mCur); diff --git a/src/platforms/esp/32/clockless_rmt_esp32.h b/src/platforms/esp/32/clockless_rmt_esp32.h index 8f5690bb..c4b28176 100644 --- a/src/platforms/esp/32/clockless_rmt_esp32.h +++ b/src/platforms/esp/32/clockless_rmt_esp32.h @@ -80,6 +80,13 @@ * send the data while the program continues to prepare the next * frame of data. * + * #define FASTLED_RMT_SERIAL_DEBUG 1 + * + * NEW (Oct 2021): If set enabled (Set to 1), output errorcodes to + * Serial for debugging if not ESP_OK. Might be useful to find + * bugs or problems with GPIO PINS. + * + * * Based on public domain code created 19 Nov 2016 by Chris Osborn <fozztexx@fozztexx.com> * http://insentricity.com * * @@ -136,7 +143,11 @@ extern void spi_flash_op_unlock(void); __attribute__ ((always_inline)) inline static uint32_t __clock_cycles() { uint32_t cyc; +#ifdef FASTLED_XTENSA __asm__ __volatile__ ("rsr %0,ccount":"=a" (cyc)); +#else + cyc = cpu_hal_get_cycle_count(); +#endif return cyc; } @@ -151,6 +162,16 @@ __attribute__ ((always_inline)) inline static uint32_t __clock_cycles() { //#define FASTLED_RMT_SHOW_TIMER false //#endif +#ifndef FASTLED_RMT_SERIAL_DEBUG +#define FASTLED_RMT_SERIAL_DEBUG 0 +#endif + +#if FASTLED_RMT_SERIAL_DEBUG == 1 +#define FASTLED_DEBUG(format, errcode, ...) if (errcode != ESP_OK) { Serial.printf(PSTR("FASTLED: " format "\n"), errcode, ##__VA_ARGS__); } +#else +#define FASTLED_DEBUG(format, errcode, ...) (void) errcode; +#endif + // -- Configuration constants #define DIVIDER 2 /* 4, 8 still seem to work, but timings become marginal */ @@ -164,11 +185,23 @@ __attribute__ ((always_inline)) inline static uint32_t __clock_cycles() { #define FASTLED_RMT_MEM_BLOCKS 2 #endif -#define MAX_PULSES (64 * FASTLED_RMT_MEM_BLOCKS) /* One block has a 64 "pulse" buffer */ +// 64 for ESP32, ESP32S2 +// 48 for ESP32S3, ESP32C3, ESP32H2 +#ifndef FASTLED_RMT_MEM_WORDS_PER_CHANNEL +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) +#define FASTLED_RMT_MEM_WORDS_PER_CHANNEL SOC_RMT_MEM_WORDS_PER_CHANNEL +#else +// ESP32 value (only chip variant supported on older IDF) +#define FASTLED_RMT_MEM_WORDS_PER_CHANNEL 64 +#endif +#endif + +#define MAX_PULSES (FASTLED_RMT_MEM_WORDS_PER_CHANNEL * FASTLED_RMT_MEM_BLOCKS) #define PULSES_PER_FILL (MAX_PULSES / 2) /* Half of the channel buffer */ // -- Convert ESP32 CPU cycles to RMT device cycles, taking into account the divider -#define F_CPU_RMT ( 80000000L) +// RMT Clock is typically APB CLK, which is 80MHz on most devices, but 40MHz on ESP32-H2 +#define F_CPU_RMT ( APB_CLK_FREQ ) #define RMT_CYCLES_PER_SEC (F_CPU_RMT/DIVIDER) #define RMT_CYCLES_PER_ESP_CYCLE (F_CPU / RMT_CYCLES_PER_SEC) #define ESP_TO_RMT_CYCLES(n) ((n) / (RMT_CYCLES_PER_ESP_CYCLE)) @@ -188,14 +221,19 @@ __attribute__ ((always_inline)) inline static uint32_t __clock_cycles() { #define FASTLED_RMT_MAX_CONTROLLERS 32 #endif -// -- Max RMT channel (default to 8 on ESP32 and 4 on ESP32-S2) +// -- Max RMT TX channel #ifndef FASTLED_RMT_MAX_CHANNELS +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) +// 8 for (ESP32) 4 for (ESP32S2, ESP32S3) 2 for (ESP32C3, ESP32H2) +#define FASTLED_RMT_MAX_CHANNELS SOC_RMT_TX_CANDIDATES_PER_GROUP +#else #ifdef CONFIG_IDF_TARGET_ESP32S2 #define FASTLED_RMT_MAX_CHANNELS 4 #else #define FASTLED_RMT_MAX_CHANNELS 8 #endif #endif +#endif class ESP32RMTController { @@ -316,8 +354,8 @@ private: // -- The actual controller object for ESP32 ESP32RMTController mRMTController; - // -- This instantiation forces a check on the pin choice - FastPin<DATA_PIN> mFastPin; + // -- Verify that the pin is valid + static_assert(FastPin<DATA_PIN>::validpin(), "Invalid pin specified"); public: diff --git a/src/platforms/esp/32/fastpin_esp32.h b/src/platforms/esp/32/fastpin_esp32.h index 7876b281..7c77a738 100644 --- a/src/platforms/esp/32/fastpin_esp32.h +++ b/src/platforms/esp/32/fastpin_esp32.h @@ -2,34 +2,45 @@ FASTLED_NAMESPACE_BEGIN -template<uint8_t PIN, uint32_t MASK> class _ESPPIN { +template<uint8_t PIN, uint32_t MASK, bool VALIDPIN> class _ESPPIN { public: typedef volatile uint32_t * port_ptr_t; typedef uint32_t port_t; - inline static void setOutput() { pinMode(PIN, OUTPUT); } + static constexpr bool validpin() { return VALIDPIN; } + +#ifndef GPIO_OUT1_REG + static constexpr uint32_t GPIO_REG = GPIO_OUT_REG; + static constexpr uint32_t GPIO_BIT_SET_REG = GPIO_OUT_W1TS_REG; + static constexpr uint32_t GPIO_BIT_CLEAR_REG = GPIO_OUT_W1TC_REG; + #else + static constexpr uint32_t GPIO_REG = PIN < 32 ? GPIO_OUT_REG : GPIO_OUT1_REG; + static constexpr uint32_t GPIO_BIT_SET_REG = PIN < 32 ? GPIO_OUT_W1TS_REG : GPIO_OUT1_W1TS_REG; + static constexpr uint32_t GPIO_BIT_CLEAR_REG = PIN < 32 ? GPIO_OUT_W1TC_REG : GPIO_OUT1_W1TC_REG; + #endif + + inline static void setOutput() { + static_assert(validpin(), "Invalid pin specified"); + pinMode(PIN, OUTPUT); + } inline static void setInput() { pinMode(PIN, INPUT); } inline static void hi() __attribute__ ((always_inline)) { - if (PIN < 32) GPIO.out_w1ts = MASK; - else GPIO.out1_w1ts.val = MASK; + *sport() = MASK; } inline static void lo() __attribute__ ((always_inline)) { - if (PIN < 32) GPIO.out_w1tc = MASK; - else GPIO.out1_w1tc.val = MASK; + *cport() = MASK; } inline static void set(register port_t val) __attribute__ ((always_inline)) { - if (PIN < 32) GPIO.out = val; - else GPIO.out1.val = val; + *port() = val; } inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } inline static void toggle() __attribute__ ((always_inline)) { - if(PIN < 32) { GPIO.out ^= MASK; } - else { GPIO.out1.val ^=MASK; } + *port() ^= MASK; } inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } @@ -37,77 +48,111 @@ public: 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)) { - if (PIN < 32) return GPIO.out | MASK; - else return GPIO.out1.val | MASK; + return (*port()) | MASK; } inline static port_t loval() __attribute__ ((always_inline)) { - if (PIN < 32) return GPIO.out & ~MASK; - else return GPIO.out1.val & ~MASK; + return (*port()) & ~MASK; } inline static port_ptr_t port() __attribute__ ((always_inline)) { - if (PIN < 32) return &GPIO.out; - else return &GPIO.out1.val; + return (port_ptr_t)GPIO_REG; } inline static port_ptr_t sport() __attribute__ ((always_inline)) { - if (PIN < 32) return &GPIO.out_w1ts; - else return &GPIO.out1_w1ts.val; + return (port_ptr_t)GPIO_BIT_SET_REG; } inline static port_ptr_t cport() __attribute__ ((always_inline)) { - if (PIN < 32) return &GPIO.out_w1tc; - else return &GPIO.out1_w1tc.val; + return (port_ptr_t)GPIO_BIT_CLEAR_REG; } inline static port_t mask() __attribute__ ((always_inline)) { return MASK; } inline static bool isset() __attribute__ ((always_inline)) { - if (PIN < 32) return GPIO.out & MASK; - else return GPIO.out1.val & MASK; + return (*port()) & MASK; } }; -#define _FL_DEFPIN(PIN) template<> class FastPin<PIN> : public _ESPPIN<PIN, ((PIN<32)?((uint32_t)1 << PIN):((uint32_t)1 << (PIN-32)))> {}; - -_FL_DEFPIN(0); -_FL_DEFPIN(1); // WARNING: Using TX causes flashiness when uploading -_FL_DEFPIN(2); -_FL_DEFPIN(3); // WARNING: Using RX causes flashiness when uploading -_FL_DEFPIN(4); -_FL_DEFPIN(5); - -// -- These pins are not safe to use: -// _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); -_FL_DEFPIN(13); -_FL_DEFPIN(14); -_FL_DEFPIN(15); -_FL_DEFPIN(16); -_FL_DEFPIN(17); -_FL_DEFPIN(18); -_FL_DEFPIN(19); - -// No pin 20 : _FL_DEFPIN(20,20); - -_FL_DEFPIN(21); // Works, but note that GPIO21 is I2C SDA -_FL_DEFPIN(22); // Works, but note that GPIO22 is I2C SCL -_FL_DEFPIN(23); - -// No pin 24 : _FL_DEFPIN(24,24); - -_FL_DEFPIN(25); -_FL_DEFPIN(26); -_FL_DEFPIN(27); - -// No pin 28-31: _FL_DEFPIN(28,28); _FL_DEFPIN(29,29); _FL_DEFPIN(30,30); _FL_DEFPIN(31,31); - -// Need special handling for pins > 31 -_FL_DEFPIN(32); -_FL_DEFPIN(33); +#ifndef FASTLED_UNUSABLE_PIN_MASK + +#define _FL_BIT(B) (1ULL << B) + +#if CONFIG_IDF_TARGET_ESP32 +// 40 GPIO pins. ESPIDF defined 24, 28-31 as invalid and 34-39 as readonly +// GPIO 6-11 used by default for SPI flash. GPIO 20 is invalid. +// NOTE: GPIO 1 & 3 commonly used for UART and may cause flashes when uploading. +#define FASTLED_UNUSABLE_PIN_MASK (0ULL | _FL_BIT(6) | _FL_BIT(7) | _FL_BIT(8) | _FL_BIT(9) | _FL_BIT(10) | _FL_BIT(20)) + +#elif CONFIG_IDF_TARGET_ESP32C3 +// 22 GPIO pins. ESPIDF defines all pins as valid +// GPIO 11-17 used by default for SPI flash +// NOTE: GPIO 20-21 commonly used for UART and may cause flashes when uploading. +#define FASTLED_UNUSABLE_PIN_MASK (0ULL | _FL_BIT(11) | _FL_BIT(12) | _FL_BIT(13) | _FL_BIT(14) | _FL_BIT(15) | _FL_BIT(16) | _FL_BIT(17)) + +#elif CONFIG_IDF_TARGET_ESP32S2 +// 48 GPIO pins. ESPIDF defines 22-25, 47 as invalid and 46-47 as readonly.s +// GPIO 27-32 used by default for SPI flash. +// NOTE: GPIO 37 & 38 commonly used for UART and may cause flashes when uploading. +#define FASTLED_UNUSABLE_PIN_MASK (0ULL | _FL_BIT(27) | _FL_BIT(28) | _FL_BIT(29) | _FL_BIT(30) | _FL_BIT(31) | _FL_BIT(32)) + +#elif CONFIG_IDF_TARGET_ESP32S3 +// 49 GPIO pins. ESPIDF defineds 22-25 as invalid. +// GPIO 27-32 used by default for SPI flash. +// NOTE: GPIO 43 & 44 commonly used for UART and may cause flashes when uploading. +#define FASTLED_UNUSABLE_PIN_MASK (0ULL | _FL_BIT(27) | _FL_BIT(28) | _FL_BIT(29) | _FL_BIT(30) | _FL_BIT(31) | _FL_BIT(32)) + +#elif CONFIG_IDF_TARGET_ESP32H2 +// 22 GPIO pins. ESPIDF defines all pins as valid. +// ESP32-H2 datasheet not yet available, when it is, mask the pins commonly used by SPI flash. +#warning ESP32-H2 chip flash configuration not yet known. Only pins defined by ESP-IDF will be masked. +#define FASTLED_UNUSABLE_PIN_MASK (0ULL) + +#else +#warning Unknown ESP32 chip variant. Only pins defined by ESP-IDF will be masked. +#define FASTLED_UNUSABLE_PIN_MASK (0ULL) +#endif + +#endif + + + +// SOC GPIO mask was not added until version IDF version 4.3. Prior to this only ESP32 chip was supported, so only +// the value for ESP32 +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 3, 0) && !defined(SOC_GPIO_VALID_OUTPUT_GPIO_MASK) +// 0~39 except from 24, 28~31 are valid +#define SOC_GPIO_VALID_GPIO_MASK (0xFFFFFFFFFFULL & ~(0ULL | _FL_BIT(24) | _FL_BIT(28) | _FL_BIT(29) | _FL_BIT(30) | _FL_BIT(31))) +// GPIO >= 34 are input only +#define SOC_GPIO_VALID_OUTPUT_GPIO_MASK (SOC_GPIO_VALID_GPIO_MASK & ~(0ULL | _FL_BIT(34) | _FL_BIT(35) | _FL_BIT(36) | _FL_BIT(37) | _FL_BIT(38) | _FL_BIT(39))) + +#endif + + +// Define mask of valid pins. Start with the list of valid output pins from ESPIDF and remove unusable pins +#define _FL_VALID_PIN_MASK (uint64_t(SOC_GPIO_VALID_OUTPUT_GPIO_MASK) & ~FASTLED_UNUSABLE_PIN_MASK) + +#define _FL_PIN_VALID(PIN) ((_FL_VALID_PIN_MASK & (1ULL << PIN)) != 0) + +#define _FL_DEFPIN(PIN) template <> class FastPin<PIN> : public _ESPPIN<PIN, ((uint32_t)1 << (PIN % 32)), _FL_PIN_VALID(PIN)> {}; + +// Define all possible pins. If the pin is not valid for a particular ESP32 variant, the pin number +// will be shifted into the 192-255 range, in effect rendering it unusable. +_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); +_FL_DEFPIN(32); _FL_DEFPIN(33); _FL_DEFPIN(34); _FL_DEFPIN(35); +_FL_DEFPIN(36); _FL_DEFPIN(37); _FL_DEFPIN(38); _FL_DEFPIN(39); +_FL_DEFPIN(40); _FL_DEFPIN(41); _FL_DEFPIN(42); _FL_DEFPIN(43); +_FL_DEFPIN(44); _FL_DEFPIN(45); _FL_DEFPIN(46); _FL_DEFPIN(47); +_FL_DEFPIN(48); _FL_DEFPIN(49); _FL_DEFPIN(50); _FL_DEFPIN(51); +_FL_DEFPIN(52); _FL_DEFPIN(53); _FL_DEFPIN(54); _FL_DEFPIN(55); +_FL_DEFPIN(56); _FL_DEFPIN(57); _FL_DEFPIN(58); _FL_DEFPIN(59); +_FL_DEFPIN(60); _FL_DEFPIN(61); _FL_DEFPIN(62); _FL_DEFPIN(63); #define HAS_HARDWARE_PIN_SUPPORT diff --git a/src/platforms/esp/32/fastspi_esp32.h b/src/platforms/esp/32/fastspi_esp32.h index 33d620fd..d69bc523 100644 --- a/src/platforms/esp/32/fastspi_esp32.h +++ b/src/platforms/esp/32/fastspi_esp32.h @@ -68,6 +68,10 @@ class ESP32SPIOutput { Selectable *m_pSelect; public: + // Verify that the pins are valid + static_assert(FastPin<DATA_PIN>::validpin(), "Invalid data pin specified"); + static_assert(FastPin<CLOCK_PIN>::validpin(), "Invalid clock pin specified"); + ESP32SPIOutput() { m_pSelect = NULL; } ESP32SPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } diff --git a/src/platforms/esp/32/led_sysdefs_esp32.h b/src/platforms/esp/32/led_sysdefs_esp32.h index 5cd374e2..cf1aa4dc 100644 --- a/src/platforms/esp/32/led_sysdefs_esp32.h +++ b/src/platforms/esp/32/led_sysdefs_esp32.h @@ -1,11 +1,26 @@ #pragma once - +#include "esp32-hal.h" #ifndef ESP32 #define ESP32 #endif #define FASTLED_ESP32 +#if CONFIG_IDF_TARGET_ARCH_RISCV +#define FASTLED_RISCV +#else +#define FASTLED_XTENSA +#endif + +// Handling for older versions of ESP32 Arduino core +#if !defined(ESP_IDF_VERSION) +// Older versions of ESP_IDF only supported ESP32 +#define CONFIG_IDF_TARGET_ESP32 1 +// Define missing version macros. Hard code older version 3.0 since actual version is unknown +#define ESP_IDF_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +#define ESP_IDF_VERSION ESP_IDF_VERSION_VAL(3, 0, 0) +#endif + // Use system millis timer #define FASTLED_HAS_MILLIS @@ -29,5 +44,3 @@ typedef unsigned long prog_uint32_t; // These can be overridden # define FASTLED_ESP32_RAW_PIN_ORDER -// #define cli() os_intr_lock(); -// #define sei() os_intr_lock(); diff --git a/src/platforms/esp/8266/clockless_esp8266.h b/src/platforms/esp/8266/clockless_esp8266.h index cf6690c6..ca2a6067 100644 --- a/src/platforms/esp/8266/clockless_esp8266.h +++ b/src/platforms/esp/8266/clockless_esp8266.h @@ -42,9 +42,7 @@ protected: #ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES ++_retry_cnt; #endif - os_intr_unlock(); delayMicroseconds(WAIT_TIME); - os_intr_lock(); } mWait.mark(); } @@ -78,23 +76,35 @@ protected: return false; } - // 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 ICACHE_RAM_ATTR showRGBInternal(PixelController<RGB_ORDER> pixels) { // Setup the pixel controller and load/scale the first byte pixels.preStepFirstByteDithering(); register uint32_t b = pixels.loadAndScale0(); - pixels.preStepFirstByteDithering(); + pixels.preStepFirstByteDithering(); uint32_t start; - { - struct Lock { - Lock() { - os_intr_lock(); - } - ~Lock() { - os_intr_unlock(); - } - }; + + // This function has multiple exits, so we'll use an object + // with a destructor that releases the interrupt lock, regardless + // of how we exit the function. It also has methods for manually + // unlocking and relocking interrupts temporarily. + struct InterruptLock { + InterruptLock() { + os_intr_lock(); + } + ~InterruptLock() { + os_intr_unlock(); + } + void Unlock() { + os_intr_unlock(); + } + void Lock() { + os_intr_lock(); + } + }; + + { // Start of interrupt-locked block + InterruptLock intlock; start = __clock_cycles(); uint32_t last_mark = start; @@ -117,14 +127,14 @@ protected: } #if (FASTLED_ALLOW_INTERRUPTS == 1) - os_intr_unlock(); + intlock.Unlock(); #endif b = pixels.advanceAndLoadAndScale0(); pixels.stepDithering(); #if (FASTLED_ALLOW_INTERRUPTS == 1) - os_intr_lock(); + intlock.Lock(); // if interrupts took longer than 45µs, punt on the current frame if((int32_t)(__clock_cycles()-last_mark) > 0) { if((int32_t)(__clock_cycles()-last_mark) > (T1+T2+T3+((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US))) { @@ -133,7 +143,7 @@ protected: } #endif }; - } + } // End of interrupt-locked block #ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES ++_frame_cnt; diff --git a/src/platforms/esp/8266/fastled_esp8266.h b/src/platforms/esp/8266/fastled_esp8266.h index 8c4048db..cb444336 100644 --- a/src/platforms/esp/8266/fastled_esp8266.h +++ b/src/platforms/esp/8266/fastled_esp8266.h @@ -1,5 +1,10 @@ #pragma once #include "fastpin_esp8266.h" + +#ifdef FASTLED_ALL_PINS_HARDWARE_SPI +#include "fastspi_esp8266.h" +#endif + #include "clockless_esp8266.h" #include "clockless_block_esp8266.h" diff --git a/src/platforms/esp/8266/fastspi_esp8266.h b/src/platforms/esp/8266/fastspi_esp8266.h new file mode 100644 index 00000000..639d3efe --- /dev/null +++ b/src/platforms/esp/8266/fastspi_esp8266.h @@ -0,0 +1,145 @@ +#pragma once +#pragma message "ESP8266 Hardware SPI support added" + +#include <SPI.h> + +FASTLED_NAMESPACE_BEGIN + +/* + * ESP8266 Hardware SPI Driver + * + * Copyright (c) 2022 Benoit Anastay + * Rewrote based on Nick Wallace, ESP32 integration. + * + * + * To enable the hardware SPI driver, add the following line *before* including + * FastLED.h: + * + * #define FASTLED_ALL_PINS_HARDWARE_SPI + * + * This driver uses the SPI bus (GPIO D5 & D7). + * + */ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint32_t SPI_SPEED> +class ESP8266SPIOutput { + Selectable *m_pSelect; + +public: + ESP8266SPIOutput() { m_pSelect = NULL; } + ESP8266SPIOutput(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) + SPI.begin(); + 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) { + SPI.transfer(b); + } + +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() { + SPI.beginTransaction(SPISettings(3200000, MSBFIRST, SPI_MODE0)); + if(m_pSelect != NULL) { m_pSelect->select(); } + } + + // release the SPI line + void release() { + if(m_pSelect != NULL) { m_pSelect->release(); } + SPI.endTransaction(); + } + + // 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) { + while(len--) { + SPI.transfer(value); + } + } + + // 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(); + uint8_t *end = data + len; + while(data != end) { + writeByte(D::adjust(*data++)); + } + 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 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) { + SPI.transfer(b); + } + + // 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; + 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(); + } +}; + +FASTLED_NAMESPACE_END
\ No newline at end of file diff --git a/src/platforms/esp/8266/led_sysdefs_esp8266.h b/src/platforms/esp/8266/led_sysdefs_esp8266.h index 26dffdcf..668a006b 100644 --- a/src/platforms/esp/8266/led_sysdefs_esp8266.h +++ b/src/platforms/esp/8266/led_sysdefs_esp8266.h @@ -35,5 +35,3 @@ typedef uint32_t prog_uint32_t; # endif #endif -// #define cli() os_intr_lock(); -// #define sei() os_intr_lock(); |