Make I2S compile

This commit is contained in:
Brian Bulkowski
2020-09-15 17:00:01 -07:00
parent 750661abcd
commit 8f0da6ff4a
5 changed files with 89 additions and 47 deletions

View File

@ -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

View File

@ -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"
)

View File

@ -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)

View File

@ -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;

View File

@ -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