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--PORTING.md2
-rw-r--r--README.md6
-rwxr-xr-xci/ci-compile12
-rw-r--r--ci/ci-flags.py3
-rw-r--r--examples/SmartMatrix/SmartMatrix.ino5
-rw-r--r--library.json3
-rw-r--r--library.properties2
-rw-r--r--release_notes.md16
-rw-r--r--src/FastLED.h12
-rw-r--r--src/controller.h6
-rw-r--r--src/fastled_delay.h15
-rw-r--r--src/fastspi.h13
-rw-r--r--src/lib8tion.h30
-rw-r--r--src/lib8tion/math8.h186
-rw-r--r--src/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h10
-rw-r--r--src/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h2
-rw-r--r--src/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h6
-rw-r--r--src/platforms/arm/mxrt1062/octows2811_controller.h64
-rw-r--r--src/platforms/avr/clockless_trinket.h31
-rw-r--r--src/platforms/esp/32/clockless_block_esp32.h5
-rw-r--r--src/platforms/esp/32/clockless_i2s_esp32.h4
-rw-r--r--src/platforms/esp/32/clockless_rmt_esp32.cpp114
-rw-r--r--src/platforms/esp/32/clockless_rmt_esp32.h48
-rw-r--r--src/platforms/esp/32/fastpin_esp32.h167
-rw-r--r--src/platforms/esp/32/fastspi_esp32.h4
-rw-r--r--src/platforms/esp/32/led_sysdefs_esp32.h19
-rw-r--r--src/platforms/esp/8266/clockless_esp8266.h44
-rw-r--r--src/platforms/esp/8266/fastled_esp8266.h5
-rw-r--r--src/platforms/esp/8266/fastspi_esp8266.h145
-rw-r--r--src/platforms/esp/8266/led_sysdefs_esp8266.h2
30 files changed, 775 insertions, 206 deletions
diff --git a/PORTING.md b/PORTING.md
index beb4e6c6..ef361016 100644
--- a/PORTING.md
+++ b/PORTING.md
@@ -41,7 +41,7 @@ The ```HAS_HARDWARE_PIN_SUPPORT``` define tells the rest of the FastLED library
## Porting fastpin.h
-The heart of the FastLED library is the fast pin accesss. This is a templated class that provides 1-2 cycle pin access, bypassing digital write and other such things. As such, this will usually be the first bit of the library that you will want to port when moving to a new platform. Once you have FastPIN up and running then you can do some basic work like testing toggles or running bit-bang'd SPI output.
+The heart of the FastLED library is the fast pin access. This is a templated class that provides 1-2 cycle pin access, bypassing digital write and other such things. As such, this will usually be the first bit of the library that you will want to port when moving to a new platform. Once you have FastPIN up and running then you can do some basic work like testing toggles or running bit-bang'd SPI output.
There's two low level FastPin classes. There's the base FastPIN template class, and then there is FastPinBB which is for bit-banded access on those MCUs that support bitbanding. Note that the bitband class is optional and primarily useful in the implementation of other functionality internal to the platform. This file is also where you would do the pin to port/bit mapping defines.
diff --git a/README.md b/README.md
index 58e7e500..1fe209c9 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
IMPORTANT NOTE: For AVR based systems, avr-gcc 4.8.x is supported and tested. This means Arduino 1.6.5 and later.
-FastLED 3.4
+FastLED 3.5
===========
This is a library for easily & efficiently controlling a wide variety of LED chipsets, like the ones
@@ -64,7 +64,7 @@ HL1606, and "595"-style shift registers are no longer supported by the library.
## Supported platforms
-Right now the library is supported on a variety of arduino compatable platforms. If it's ARM or AVR and uses the arduino software (or a modified version of it to build) then it is likely supported. Note that we have a long list of upcoming platforms to support, so if you don't see what you're looking for here, ask, it may be on the roadmap (or may already be supported). N.B. at the moment we are only supporting the stock compilers that ship with the arduino software. Support for upgraded compilers, as well as using AVR studio and skipping the arduino entirely, should be coming in a near future release.
+Right now the library is supported on a variety of arduino compatible platforms. If it's ARM or AVR and uses the arduino software (or a modified version of it to build) then it is likely supported. Note that we have a long list of upcoming platforms to support, so if you don't see what you're looking for here, ask, it may be on the roadmap (or may already be supported). N.B. at the moment we are only supporting the stock compilers that ship with the arduino software. Support for upgraded compilers, as well as using AVR studio and skipping the arduino entirely, should be coming in a near future release.
* Arduino & compatibles - straight up arduino devices, uno, duo, leonardo, mega, nano, etc...
* Arduino Yún
@@ -82,7 +82,7 @@ What types of platforms are we thinking about supporting in the future? Here's
## What about that name?
-Wait, what happend to FastSPI_LED and FastSPI_LED2? The library was initially named FastSPI_LED because it was focused on very fast and efficient SPI access. However, since then, the library has expanded to support a number of LED chipsets that don't use SPI, as well as a number of math and utility functions for LED processing across the board. We decided that the name FastLED more accurately represents the totality of what the library provides, everything fast, for LEDs.
+Wait, what happened to FastSPI_LED and FastSPI_LED2? The library was initially named FastSPI_LED because it was focused on very fast and efficient SPI access. However, since then, the library has expanded to support a number of LED chipsets that don't use SPI, as well as a number of math and utility functions for LED processing across the board. We decided that the name FastLED more accurately represents the totality of what the library provides, everything fast, for LEDs.
## For more information
diff --git a/ci/ci-compile b/ci/ci-compile
index 208a173a..a7a21f2c 100755
--- a/ci/ci-compile
+++ b/ci/ci-compile
@@ -8,12 +8,12 @@
# [BOARDS=boards] [EXAMPLES=examples] ./ci-compile
#
# e.g.
-# $ ./compile-ci
+# $ ./compile-ci
# - compile all board/examples combinations
#
-# $ BOARDS="esp32 esp01" EXAMPLES=Blink ./compile-ci
+# $ BOARDS="esp32 esp01" EXAMPLES=Blink ./compile-ci
# - compile only Blink example for the esp32 and esp8266 platforms
-#
+#
set -eou pipefail
# List of examples that will be compiled by default
@@ -30,7 +30,9 @@ BOARD_OPTS=$(for b in $BOARDS; do echo -n "--board $b "; done)
cd "$DIR/.."
-for d in $EXAMPLES ; do
+export PLATFORMIO_EXTRA_SCRIPTS="pre:lib/ci/ci-flags.py"
+
+for d in $EXAMPLES ; do
echo "*** building example $d for $BOARDS ***"
- pio ci $BOARD_OPTS --lib=src "examples/$d/"*ino
+ pio ci $BOARD_OPTS --lib=ci --lib=src "examples/$d/"*ino
done
diff --git a/ci/ci-flags.py b/ci/ci-flags.py
new file mode 100644
index 00000000..044fdb1c
--- /dev/null
+++ b/ci/ci-flags.py
@@ -0,0 +1,3 @@
+Import("env")
+
+env.Append(CXXFLAGS=["-Wno-register"])
diff --git a/examples/SmartMatrix/SmartMatrix.ino b/examples/SmartMatrix/SmartMatrix.ino
index e685cf67..dc3e7eee 100644
--- a/examples/SmartMatrix/SmartMatrix.ino
+++ b/examples/SmartMatrix/SmartMatrix.ino
@@ -1,3 +1,8 @@
+/* This example demos a rectangular LED matrix with moving noise.
+ It requires the SmartMatrix library in addition to FastLED.
+ This SmartMatrix library is only available on Teensy boards at the moment.
+ It can be found at https://github.com/pixelmatix/SmartMatrix
+*/
#include <SmartMatrix.h>
#include <FastLED.h>
diff --git a/library.json b/library.json
index 9e330204..067bcadc 100644
--- a/library.json
+++ b/library.json
@@ -33,11 +33,12 @@
"type": "git",
"url": "https://github.com/FastLED/FastLED.git"
},
- "version": "3.4.0",
+ "version": "3.5.0",
"license": "MIT",
"homepage": "http://fastled.io",
"frameworks": "arduino",
"platforms": "atmelavr, atmelsam, freescalekinetis, nordicnrf51, nxplpc, ststm32, teensy, espressif8266, espressif32, nordicnrf52",
+ "headers": "FastLED.h",
"export": {
"exclude": [
"docs",
diff --git a/library.properties b/library.properties
index fc8d17b1..6694984e 100644
--- a/library.properties
+++ b/library.properties
@@ -1,5 +1,5 @@
name=FastLED
-version=3.4.0
+version=3.5.0
author=Daniel Garcia
maintainer=Daniel Garcia <dgarcia@fastled.io>
sentence=Multi-platform library for controlling dozens of different types of LEDs along with optimized math, effect, and noise functions.
diff --git a/release_notes.md b/release_notes.md
index e7eb7792..03e48c1d 100644
--- a/release_notes.md
+++ b/release_notes.md
@@ -1,3 +1,16 @@
+FastLED 3.5.0
+=============
+This release incorporates dozens of valuable improvements from FastLED contributors, tested and explored by the world-wide FastLED community of artists, creators, and developers. Thank you for all of your time, energy, and help! Here are some of the most significant changes in FastLED 3.5.0:
+* Greatly improved ESP32 and ESP8266 support
+* Improved board support for Teensy 4, Adafruit MatrixPortal M4, Arduino Nano Every, Partical Photon, and Seeed Wio Terminal
+* Improved and/or sped up: sin8, cos8, blend8, blur2d, scale8, Perlin/simplex noise
+* Improved HSV colors are smoother, richer, and brighter in fill_rainbow and elsewhere
+* Modernized and cleaned up the FastLED examples
+* Added github CI integration to help with automated testing
+* Added a Code of Conduct from https://www.contributor-covenant.org/
+* Released January 2022, with many thanks to FastLED contributors and the FastLED community!
+
+
FastLED 3.4.0
=============
@@ -12,6 +25,9 @@ FastLED 3.4.0
* Many small code cleanups and bug fixes
* Released December 2020, with many thanks to everyone contributing to FastLED!
+We also want to note here that in 2020, Github named FastLED one of the 'Greatest Hits' of Open Source software, and preserved an archived copy of FastLED in the Arctic Code Vault, the Bodleian Library at Oxford University, the Bibliotheca Alexandrina (the Library of Alexandria), and the Stanford University Libraries. https://archiveprogram.github.com/greatest-hits/
+
+
FastLED 3.3.3
=============
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();