mirror of
https://github.com/bbulkow/FastLED-idf.git
synced 2025-07-29 18:28:23 +02:00
Make I2S compile
This commit is contained in:
38
README.md
38
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
|
||||
|
@ -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"
|
||||
)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user