diff options
-rw-r--r-- | library.json | 1 | ||||
-rw-r--r-- | src/lib8tion.h | 10 | ||||
-rw-r--r-- | src/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h | 10 | ||||
-rw-r--r-- | src/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h | 2 | ||||
-rw-r--r-- | src/platforms/arm/mxrt1062/octows2811_controller.h | 64 | ||||
-rw-r--r-- | src/platforms/esp/32/clockless_block_esp32.h | 5 | ||||
-rw-r--r-- | src/platforms/esp/32/clockless_i2s_esp32.h | 4 | ||||
-rw-r--r-- | src/platforms/esp/32/clockless_rmt_esp32.cpp | 114 | ||||
-rw-r--r-- | src/platforms/esp/32/clockless_rmt_esp32.h | 48 | ||||
-rw-r--r-- | src/platforms/esp/32/fastpin_esp32.h | 167 | ||||
-rw-r--r-- | src/platforms/esp/32/fastspi_esp32.h | 4 | ||||
-rw-r--r-- | src/platforms/esp/32/led_sysdefs_esp32.h | 19 | ||||
-rw-r--r-- | src/platforms/esp/8266/clockless_esp8266.h | 44 | ||||
-rw-r--r-- | src/platforms/esp/8266/led_sysdefs_esp8266.h | 2 |
14 files changed, 379 insertions, 115 deletions
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/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/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..d0000a40 --- /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 (uint32_t i = 0; i < 8; i++) { + uint8_t r = pixels.loadAndScale0(i); + uint8_t g = pixels.loadAndScale1(i); + uint8_t b = pixels.loadAndScale2(i); + 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/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(); |