diff --git a/README.md b/README.md index e06fe5b..0e4ed1b 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,10 @@ This port of FastLED 3.3 runs under the 4.x ESP-IDF development environment. Enjoy. -MASSIVE UPDATE Sept 4, 2020. Even after porting Sam Guyer's branch in July, I still -had a huge amount of visual artifacts. I've done a huge analysis and have licked the issue to my -satisfaction, and can say the system simply doesn't glitch. - -UPDATE 2 Sept 11, 2020. Added the WS2812FX pattern library, see below. +Updates: Aug, Sept 2020: +- RMT interface well tested. +- WS2812FX library ported and working. +- I2S hardware working ( see below ) There are some new tunables, and if you're also fighting glitches, you need to read `components/FastLED-idf/ESP-IDF.md`. @@ -53,6 +52,17 @@ Playing around, there are "a lot of nice libraries on FastLED", but each and eve requires porting. At least, now there's a single one to start with. See the underlying component, and use it or not. If you don't want it, just don't bring that component over into your project. +# I2S vs RMT + +At first, I focused the port on RMT, as it seemed the hardware everyone talked about. As you see +below, the RMT interface even has a Espressif provided example! Thus the journey of getting the MEM_BUFS +code working and soak up the interrupt latency. + +But, the I2S hardware is almost certainly cooler than RMT. It has more parallelism, and less code. + +Right now, the code is still checked in with default RMT simply because I haven't tested I2S as much, +but please see the I2S section below on enabling it. + # TL;DR about this repo As with any ESP-IDF project, there is a sdkconfig file. It contains things that might @@ -80,7 +90,7 @@ and using a 4-pin package isn't so cool as an 8 pin package, since an ESP32 can 8 channels. That's SN74HCT245N , and they're to be had from Digikey at $0.60. Read the datasheet carefully and get the direction and the enable pins right - they can't float. -# Use of ESP32 hardware (RMT) for 3 wire LEDs +# Use of ESP32 RMT hardware for 3 wire LEDs The ESP32 has an interesting module, called RMT. It's a module that's meant to make arbitrary waveforms on pins, without having to bang each pin at @@ -115,6 +125,22 @@ and if you want to use the direct mode, you should turn off the driver. No extra commands in `menuconfig` seem necessary. +# Use of ESP32 I2S hardware for 3 wire LEDs + +## TL;DR enable it + +There's a `#define` at the top of fastled.h which chooses I2S or RMT. Switch to I2S. + +Comment out `"platforms/esp/32/clockless_rmt_esp32.cpp"` from `components\FastLED-idf\CMakeLists.txt` + +You need to remove the RMT file from the compile because you don't want to waste RAM, and there +are colliding symbols. You can't use both because there's no current way to define which strings +use which hardware. + +## Which to use? + +Not sure yet. Going to play with it on different builds and see what works. + # Four wire LEDs ( APA102 and similar ) Interestingly, four wire LEDs can't use the RMT interface, because diff --git a/components/FastLED-idf/CMakeLists.txt b/components/FastLED-idf/CMakeLists.txt index c27873f..8788f57 100644 --- a/components/FastLED-idf/CMakeLists.txt +++ b/components/FastLED-idf/CMakeLists.txt @@ -13,6 +13,7 @@ set(srcs "wiring.cpp" "hal/esp32-hal-misc.c" "hal/esp32-hal-gpio.c" +# remove the following if you want I2S instead of RMT hardware, just put a pound in front "platforms/esp/32/clockless_rmt_esp32.cpp" ) diff --git a/components/FastLED-idf/FastLED.h b/components/FastLED-idf/FastLED.h index ba6028a..1c7123e 100644 --- a/components/FastLED-idf/FastLED.h +++ b/components/FastLED-idf/FastLED.h @@ -8,6 +8,10 @@ #define ESP32 #define FASTLED_NO_PINMAP +// prefer I2S? Comment this in. +// Not the default because haven't tried it as much, does work +// #define FASTLED_ESP32_I2S + #include "esp32-hal.h" #if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) diff --git a/components/FastLED-idf/platforms/esp/32/clockless_i2s_esp32.h b/components/FastLED-idf/platforms/esp/32/clockless_i2s_esp32.h index 6fd067b..0d4188e 100644 --- a/components/FastLED-idf/platforms/esp/32/clockless_i2s_esp32.h +++ b/components/FastLED-idf/platforms/esp/32/clockless_i2s_esp32.h @@ -86,7 +86,8 @@ #pragma once -#pragma message "NOTE: ESP32 support using I2S parallel driver. All strips must use the same chipset" +// This is way too noisy. Is output a LARGE NUMBER of times. +// #pragma message "NOTE: ESP32 support using I2S parallel driver. All strips must use the same chipset" FASTLED_NAMESPACE_BEGIN @@ -95,6 +96,10 @@ extern "C" { #endif #include "esp_heap_caps.h" +#include "esp_intr_alloc.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + #include "soc/soc.h" #include "soc/gpio_sig_map.h" #include "soc/i2s_reg.h" @@ -102,8 +107,8 @@ extern "C" { #include "soc/io_mux_reg.h" #include "driver/gpio.h" #include "driver/periph_ctrl.h" -#include "rom/lldesc.h" -#include "esp_intr_alloc.h" +#include "esp32/rom/lldesc.h" + #include "esp_log.h" #ifdef __cplusplus @@ -131,7 +136,7 @@ __attribute__ ((always_inline)) inline static uint32_t __clock_cycles() { // -- I2S clock #define I2S_BASE_CLK (80000000L) -#define I2S_MAX_CLK (20000000L) //more tha a certain speed and the I2s looses some bits +#define I2S_MAX_CLK (20000000L) //more tha a certain speed and the I2s loses some bits #define I2S_MAX_PULSE_PER_BIT 20 //put it higher to get more accuracy but it could decrease the refresh rate without real improvement // -- Convert ESP32 cycles back into nanoseconds #define ESPCLKS_TO_NS(_CLKS) (((long)(_CLKS) * 1000L) / F_CPU_MHZ) @@ -262,16 +267,16 @@ protected: static void initBitPatterns() { // Precompute the bit patterns based on the I2S sample rate - // Serial.println("Setting up fastled using I2S"); + // println("Setting up fastled using I2S"); // -- First, convert back to ns from CPU clocks uint32_t T1ns = ESPCLKS_TO_NS(T1); uint32_t T2ns = ESPCLKS_TO_NS(T2); uint32_t T3ns = ESPCLKS_TO_NS(T3); - // Serial.print("T1 = "); Serial.print(T1); Serial.print(" ns "); Serial.println(T1ns); - // Serial.print("T2 = "); Serial.print(T2); Serial.print(" ns "); Serial.println(T2ns); - // Serial.print("T3 = "); Serial.print(T3); Serial.print(" ns "); Serial.println(T3ns); + // print("T1 = "); print(T1); print(" ns "); println(T1ns); + // print("T2 = "); print(T2); print(" ns "); println(T2ns); + // print("T3 = "); print(T3); print(" ns "); println(T3ns); /* We calculate the best pcgd to the timing @@ -287,21 +292,21 @@ protected: if(smallest>T3) smallest=T3; double freq=(double)1/(double)(T1ns + T2ns + T3ns); - // Serial.printf("chipset frequency:%f Khz\n", 1000000L*freq); - // Serial.printf("smallest %d\n",smallest); + // printf("chipset frequency:%f Khz\n", 1000000L*freq); + // printf("smallest %d\n",smallest); int pgc_=1; int precision=0; pgc_=pgcd(smallest,precision,T1,T2,T3); - //Serial.printf("%f\n",I2S_MAX_CLK/(1000000000L*freq)); + //printf("%f\n",I2S_MAX_CLK/(1000000000L*freq)); while(pgc_==1 || (T1/pgc_ +T2/pgc_ +T3/pgc_)>I2S_MAX_PULSE_PER_BIT) //while(pgc_==1 || (T1/pgc_ +T2/pgc_ +T3/pgc_)>I2S_MAX_CLK/(1000000000L*freq)) { precision++; pgc_=pgcd(smallest,precision,T1,T2,T3); - //Serial.printf("%d %d\n",pgc_,(a+b+c)/pgc_); + //printf("%d %d\n",pgc_,(a+b+c)/pgc_); } pgc_=pgcd(smallest,precision,T1,T2,T3); - // Serial.printf("pgcd %d precision:%d\n",pgc_,precision); - // Serial.printf("nb pulse per bit:%d\n",T1/pgc_ +T2/pgc_ +T3/pgc_); + // printf("pgcd %d precision:%d\n",pgc_,precision); + // printf("nb pulse per bit:%d\n",T1/pgc_ +T2/pgc_ +T3/pgc_); gPulsesPerBit=(int)T1/pgc_ +(int)T2/pgc_ +(int)T3/pgc_; /* we calculate the duration of one pulse nd htre base frequency of the led @@ -312,7 +317,7 @@ protected: */ freq=1000000000L*freq*gPulsesPerBit; - // Serial.printf("needed frequency (nbpiulse per bit)*(chispset frequency):%f Mhz\n",freq/1000000); + // printf("needed frequency (nbpiulse per bit)*(chispset frequency):%f Mhz\n",freq/1000000); /* we do calculate the needed N a and b @@ -321,7 +326,7 @@ protected: */ - CLOCK_DIVIDER_N=(int)((double)I2S_BASE_CLK/freq); + CLOCK_DIVIDER_N=(int)((double)I2S_BASE_CLK/freq); double v=I2S_BASE_CLK/freq-CLOCK_DIVIDER_N; double prec=(double)1/63; @@ -362,23 +367,23 @@ protected: } //printf("%d %d %f %f %d\n",CLOCK_DIVIDER_B,CLOCK_DIVIDER_A,(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A,v,CLOCK_DIVIDER_N); - //Serial.printf("freq %f %f\n",freq,I2S_BASE_CLK/(CLOCK_DIVIDER_N+(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A)); + //printf("freq %f %f\n",freq,I2S_BASE_CLK/(CLOCK_DIVIDER_N+(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A)); freq=1/(CLOCK_DIVIDER_N+(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A); freq=freq*I2S_BASE_CLK; - // Serial.printf("calculted for i2s frequency:%f Mhz N:%d B:%d A:%d\n",freq/1000000,CLOCK_DIVIDER_N,CLOCK_DIVIDER_B,CLOCK_DIVIDER_A); + // printf("calculted for i2s frequency:%f Mhz N:%d B:%d A:%d\n",freq/1000000,CLOCK_DIVIDER_N,CLOCK_DIVIDER_B,CLOCK_DIVIDER_A); // double pulseduration=1000000000/freq; - // Serial.printf("Pulse duration: %f ns\n",pulseduration); + // printf("Pulse duration: %f ns\n",pulseduration); // gPulsesPerBit = (T1ns + T2ns + T3ns)/FASTLED_I2S_NS_PER_PULSE; - //Serial.print("Pulses per bit: "); Serial.println(gPulsesPerBit); + //print("Pulses per bit: "); println(gPulsesPerBit); //int ones_for_one = ((T1ns + T2ns - 1)/FASTLED_I2S_NS_PER_PULSE) + 1; ones_for_one = T1/pgc_ +T2/pgc_; - //Serial.print("One bit: target "); - //Serial.print(T1ns+T2ns); Serial.print("ns --- "); - //Serial.print(ones_for_one); Serial.print(" 1 bits"); - //Serial.print(" = "); Serial.print(ones_for_one * FASTLED_I2S_NS_PER_PULSE); Serial.println("ns"); - // Serial.printf("one bit : target %d ns --- %d pulses 1 bit = %f ns\n",T1ns+T2ns,ones_for_one ,ones_for_one*pulseduration); + //print("One bit: target "); + //print(T1ns+T2ns); print("ns --- "); + //print(ones_for_one); print(" 1 bits"); + //print(" = "); print(ones_for_one * FASTLED_I2S_NS_PER_PULSE); println("ns"); + // printf("one bit : target %d ns --- %d pulses 1 bit = %f ns\n",T1ns+T2ns,ones_for_one ,ones_for_one*pulseduration); int i = 0; @@ -393,11 +398,11 @@ protected: //int ones_for_zero = ((T1ns - 1)/FASTLED_I2S_NS_PER_PULSE) + 1; ones_for_zero =T1/pgc_ ; - // Serial.print("Zero bit: target "); - // Serial.print(T1ns); Serial.print("ns --- "); - //Serial.print(ones_for_zero); Serial.print(" 1 bits"); - //Serial.print(" = "); Serial.print(ones_for_zero * FASTLED_I2S_NS_PER_PULSE); Serial.println("ns"); - // Serial.printf("Zero bit : target %d ns --- %d pulses 1 bit = %f ns\n",T1ns,ones_for_zero ,ones_for_zero*pulseduration); + // print("Zero bit: target "); + // print(T1ns); print("ns --- "); + //print(ones_for_zero); print(" 1 bits"); + //print(" = "); print(ones_for_zero * FASTLED_I2S_NS_PER_PULSE); println("ns"); + // printf("Zero bit : target %d ns --- %d pulses 1 bit = %f ns\n",T1ns,ones_for_zero ,ones_for_zero*pulseduration); i = 0; while ( i < ones_for_zero ) { gZeroBit[i] = 0xFFFFFF00; @@ -513,8 +518,10 @@ protected: // -- Allocate i2s interrupt SET_PERI_REG_BITS(I2S_INT_ENA_REG(I2S_DEVICE), I2S_OUT_EOF_INT_ENA_V, 1, I2S_OUT_EOF_INT_ENA_S); - esp_intr_alloc(interruptSource, 0, // ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3, - &interruptHandler, 0, &gI2S_intr_handle); + ESP_ERROR_CHECK( + esp_intr_alloc(interruptSource, 0 /* ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3*/, + &interruptHandler, 0, &gI2S_intr_handle) + ); // -- Create a semaphore to block execution until all the controllers are done if (gTX_sem == NULL) { @@ -522,7 +529,7 @@ protected: xSemaphoreGive(gTX_sem); } - // Serial.println("Init I2S"); + // println("Init I2S"); gInitialized = true; } @@ -562,8 +569,8 @@ protected: // -- Keep track of the number of strips we've seen gNumStarted++; - // Serial.print("Show pixels "); - // Serial.println(gNumStarted); + // print("Show pixels "); + // println(gNumStarted); // -- The last call to showPixels is the one responsible for doing // all of the actual work @@ -619,7 +626,7 @@ protected: * from each strip), transpose and encode the bits, and store * them in the DMA buffer for the I2S peripheral to read. */ - static void fillBuffer() + static IRAM_ATTR void fillBuffer() { // -- Alternate between buffers volatile uint32_t * buf = (uint32_t *) dmaBuffers[gCurBuffer]->buffer; @@ -659,7 +666,7 @@ protected: // -- Tranpose each array: all the bit 7's, then all the bit 6's, ... transpose32(gPixelRow[channel], gPixelBits[channel][0] ); - //Serial.print("Channel: "); Serial.print(channel); Serial.print(" "); + //print("Channel: "); print(channel); print(" "); for (int bitnum = 0; bitnum < 8; bitnum++) { uint8_t * row = (uint8_t *) (gPixelBits[channel][bitnum]); uint32_t bit = (row[0] << 24) | (row[1] << 16) | (row[2] << 8) | row[3]; @@ -717,9 +724,9 @@ protected: static void i2sStart() { // esp_intr_disable(gI2S_intr_handle); - // Serial.println("I2S start"); + // println("I2S start"); i2sReset(); - //Serial.println(dmaBuffers[0]->sampleCount()); + //println(dmaBuffers[0]->sampleCount()); i2s->lc_conf.val=I2S_OUT_DATA_BURST_EN | I2S_OUTDSCR_BURST_EN | I2S_OUT_DATA_BURST_EN; i2s->out_link.addr = (uint32_t) & (dmaBuffers[0]->descriptor); i2s->out_link.start = 1; @@ -740,7 +747,7 @@ protected: static void i2sReset() { - // Serial.println("I2S reset"); + // println("I2S reset"); const unsigned long lc_conf_reset_flags = I2S_IN_RST_M | I2S_OUT_RST_M | I2S_AHBM_RST_M | I2S_AHBM_FIFO_RST_M; i2s->lc_conf.val |= lc_conf_reset_flags; i2s->lc_conf.val &= ~lc_conf_reset_flags; @@ -764,7 +771,7 @@ protected: static void i2sStop() { - // Serial.println("I2S stop"); + // println("I2S stop"); esp_intr_disable(gI2S_intr_handle); i2sReset(); i2s->conf.rx_start = 0; diff --git a/components/FastLED-idf/platforms/esp/32/fastled_esp32.h b/components/FastLED-idf/platforms/esp/32/fastled_esp32.h index edf27e7..6e9a1c5 100644 --- a/components/FastLED-idf/platforms/esp/32/fastled_esp32.h +++ b/components/FastLED-idf/platforms/esp/32/fastled_esp32.h @@ -2,6 +2,10 @@ #include "fastpin_esp32.h" +#ifdef FASTLED_ALL_PINS_HARDWARE_SPI +#include "fastspi_esp32.h" +#endif + #ifdef FASTLED_ESP32_I2S #include "clockless_i2s_esp32.h" #else