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.md4
-rw-r--r--library.json1
-rw-r--r--src/controller.h6
-rw-r--r--src/lib8tion.h10
-rw-r--r--src/lib8tion/math8.h89
-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/octows2811_controller.h64
-rw-r--r--src/platforms/avr/clockless_trinket.h23
-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/led_sysdefs_esp8266.h2
19 files changed, 466 insertions, 152 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..52067b20 100644
--- a/README.md
+++ b/README.md
@@ -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/library.json b/library.json
index 9e330204..fdcde146 100644
--- a/library.json
+++ b/library.json
@@ -38,6 +38,7 @@
"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/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/lib8tion.h b/src/lib8tion.h
index 0cc3baa4..80e27100 100644
--- a/src/lib8tion.h
+++ b/src/lib8tion.h
@@ -834,10 +834,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 +843,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..f95697bd 100644
--- a/src/lib8tion/math8.h
+++ b/src/lib8tion/math8.h
@@ -469,32 +469,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 +555,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/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 3f333435..a8e73930 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
@@ -120,7 +127,7 @@ protected:
}
// 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)
@@ -243,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)
@@ -301,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"
@@ -370,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)];
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..66b04928 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, ...)
+#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/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();