Skip to content

Commit 8f0da6f

Browse files
committed
Make I2S compile
1 parent 750661a commit 8f0da6f

File tree

5 files changed

+89
-47
lines changed

5 files changed

+89
-47
lines changed

README.md

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44

55
This port of FastLED 3.3 runs under the 4.x ESP-IDF development environment. Enjoy.
66

7-
MASSIVE UPDATE Sept 4, 2020. Even after porting Sam Guyer's branch in July, I still
8-
had a huge amount of visual artifacts. I've done a huge analysis and have licked the issue to my
9-
satisfaction, and can say the system simply doesn't glitch.
10-
11-
UPDATE 2 Sept 11, 2020. Added the WS2812FX pattern library, see below.
7+
Updates: Aug, Sept 2020:
8+
- RMT interface well tested.
9+
- WS2812FX library ported and working.
10+
- I2S hardware working ( see below )
1211

1312
There are some new tunables, and if you're also fighting glitches, you need to read `components/FastLED-idf/ESP-IDF.md`.
1413

@@ -53,6 +52,17 @@ Playing around, there are "a lot of nice libraries on FastLED", but each and eve
5352
requires porting. At least, now there's a single one to start with. See the underlying component,
5453
and use it or not. If you don't want it, just don't bring that component over into your project.
5554

55+
# I2S vs RMT
56+
57+
At first, I focused the port on RMT, as it seemed the hardware everyone talked about. As you see
58+
below, the RMT interface even has a Espressif provided example! Thus the journey of getting the MEM_BUFS
59+
code working and soak up the interrupt latency.
60+
61+
But, the I2S hardware is almost certainly cooler than RMT. It has more parallelism, and less code.
62+
63+
Right now, the code is still checked in with default RMT simply because I haven't tested I2S as much,
64+
but please see the I2S section below on enabling it.
65+
5666
# TL;DR about this repo
5767

5868
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
8090
8 channels. That's SN74HCT245N , and they're to be had from Digikey at $0.60. Read the datasheet
8191
carefully and get the direction and the enable pins right - they can't float.
8292

83-
# Use of ESP32 hardware (RMT) for 3 wire LEDs
93+
# Use of ESP32 RMT hardware for 3 wire LEDs
8494

8595
The ESP32 has an interesting module, called RMT. It's a module that's
8696
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.
115125

116126
No extra commands in `menuconfig` seem necessary.
117127

128+
# Use of ESP32 I2S hardware for 3 wire LEDs
129+
130+
## TL;DR enable it
131+
132+
There's a `#define` at the top of fastled.h which chooses I2S or RMT. Switch to I2S.
133+
134+
Comment out `"platforms/esp/32/clockless_rmt_esp32.cpp"` from `components\FastLED-idf\CMakeLists.txt`
135+
136+
You need to remove the RMT file from the compile because you don't want to waste RAM, and there
137+
are colliding symbols. You can't use both because there's no current way to define which strings
138+
use which hardware.
139+
140+
## Which to use?
141+
142+
Not sure yet. Going to play with it on different builds and see what works.
143+
118144
# Four wire LEDs ( APA102 and similar )
119145

120146
Interestingly, four wire LEDs can't use the RMT interface, because

components/FastLED-idf/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ set(srcs
1313
"wiring.cpp"
1414
"hal/esp32-hal-misc.c"
1515
"hal/esp32-hal-gpio.c"
16+
# remove the following if you want I2S instead of RMT hardware, just put a pound in front
1617
"platforms/esp/32/clockless_rmt_esp32.cpp"
1718
)
1819

components/FastLED-idf/FastLED.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
#define ESP32
99
#define FASTLED_NO_PINMAP
1010

11+
// prefer I2S? Comment this in.
12+
// Not the default because haven't tried it as much, does work
13+
// #define FASTLED_ESP32_I2S
14+
1115
#include "esp32-hal.h"
1216

1317
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)

components/FastLED-idf/platforms/esp/32/clockless_i2s_esp32.h

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@
8686

8787
#pragma once
8888

89-
#pragma message "NOTE: ESP32 support using I2S parallel driver. All strips must use the same chipset"
89+
// This is way too noisy. Is output a LARGE NUMBER of times.
90+
// #pragma message "NOTE: ESP32 support using I2S parallel driver. All strips must use the same chipset"
9091

9192
FASTLED_NAMESPACE_BEGIN
9293

@@ -95,15 +96,19 @@ extern "C" {
9596
#endif
9697

9798
#include "esp_heap_caps.h"
99+
#include "esp_intr_alloc.h"
100+
#include "freertos/task.h"
101+
#include "freertos/semphr.h"
102+
98103
#include "soc/soc.h"
99104
#include "soc/gpio_sig_map.h"
100105
#include "soc/i2s_reg.h"
101106
#include "soc/i2s_struct.h"
102107
#include "soc/io_mux_reg.h"
103108
#include "driver/gpio.h"
104109
#include "driver/periph_ctrl.h"
105-
#include "rom/lldesc.h"
106-
#include "esp_intr_alloc.h"
110+
#include "esp32/rom/lldesc.h"
111+
107112
#include "esp_log.h"
108113

109114
#ifdef __cplusplus
@@ -131,7 +136,7 @@ __attribute__ ((always_inline)) inline static uint32_t __clock_cycles() {
131136

132137
// -- I2S clock
133138
#define I2S_BASE_CLK (80000000L)
134-
#define I2S_MAX_CLK (20000000L) //more tha a certain speed and the I2s looses some bits
139+
#define I2S_MAX_CLK (20000000L) //more tha a certain speed and the I2s loses some bits
135140
#define I2S_MAX_PULSE_PER_BIT 20 //put it higher to get more accuracy but it could decrease the refresh rate without real improvement
136141
// -- Convert ESP32 cycles back into nanoseconds
137142
#define ESPCLKS_TO_NS(_CLKS) (((long)(_CLKS) * 1000L) / F_CPU_MHZ)
@@ -262,16 +267,16 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
262267
static void initBitPatterns()
263268
{
264269
// Precompute the bit patterns based on the I2S sample rate
265-
// Serial.println("Setting up fastled using I2S");
270+
// println("Setting up fastled using I2S");
266271

267272
// -- First, convert back to ns from CPU clocks
268273
uint32_t T1ns = ESPCLKS_TO_NS(T1);
269274
uint32_t T2ns = ESPCLKS_TO_NS(T2);
270275
uint32_t T3ns = ESPCLKS_TO_NS(T3);
271276

272-
// Serial.print("T1 = "); Serial.print(T1); Serial.print(" ns "); Serial.println(T1ns);
273-
// Serial.print("T2 = "); Serial.print(T2); Serial.print(" ns "); Serial.println(T2ns);
274-
// Serial.print("T3 = "); Serial.print(T3); Serial.print(" ns "); Serial.println(T3ns);
277+
// print("T1 = "); print(T1); print(" ns "); println(T1ns);
278+
// print("T2 = "); print(T2); print(" ns "); println(T2ns);
279+
// print("T3 = "); print(T3); print(" ns "); println(T3ns);
275280

276281
/*
277282
We calculate the best pcgd to the timing
@@ -287,21 +292,21 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
287292
if(smallest>T3)
288293
smallest=T3;
289294
double freq=(double)1/(double)(T1ns + T2ns + T3ns);
290-
// Serial.printf("chipset frequency:%f Khz\n", 1000000L*freq);
291-
// Serial.printf("smallest %d\n",smallest);
295+
// printf("chipset frequency:%f Khz\n", 1000000L*freq);
296+
// printf("smallest %d\n",smallest);
292297
int pgc_=1;
293298
int precision=0;
294299
pgc_=pgcd(smallest,precision,T1,T2,T3);
295-
//Serial.printf("%f\n",I2S_MAX_CLK/(1000000000L*freq));
300+
//printf("%f\n",I2S_MAX_CLK/(1000000000L*freq));
296301
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))
297302
{
298303
precision++;
299304
pgc_=pgcd(smallest,precision,T1,T2,T3);
300-
//Serial.printf("%d %d\n",pgc_,(a+b+c)/pgc_);
305+
//printf("%d %d\n",pgc_,(a+b+c)/pgc_);
301306
}
302307
pgc_=pgcd(smallest,precision,T1,T2,T3);
303-
// Serial.printf("pgcd %d precision:%d\n",pgc_,precision);
304-
// Serial.printf("nb pulse per bit:%d\n",T1/pgc_ +T2/pgc_ +T3/pgc_);
308+
// printf("pgcd %d precision:%d\n",pgc_,precision);
309+
// printf("nb pulse per bit:%d\n",T1/pgc_ +T2/pgc_ +T3/pgc_);
305310
gPulsesPerBit=(int)T1/pgc_ +(int)T2/pgc_ +(int)T3/pgc_;
306311
/*
307312
we calculate the duration of one pulse nd htre base frequency of the led
@@ -312,7 +317,7 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
312317
*/
313318

314319
freq=1000000000L*freq*gPulsesPerBit;
315-
// Serial.printf("needed frequency (nbpiulse per bit)*(chispset frequency):%f Mhz\n",freq/1000000);
320+
// printf("needed frequency (nbpiulse per bit)*(chispset frequency):%f Mhz\n",freq/1000000);
316321

317322
/*
318323
we do calculate the needed N a and b
@@ -321,7 +326,7 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
321326
322327
*/
323328

324-
CLOCK_DIVIDER_N=(int)((double)I2S_BASE_CLK/freq);
329+
CLOCK_DIVIDER_N=(int)((double)I2S_BASE_CLK/freq);
325330
double v=I2S_BASE_CLK/freq-CLOCK_DIVIDER_N;
326331

327332
double prec=(double)1/63;
@@ -362,23 +367,23 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
362367
}
363368

364369
//printf("%d %d %f %f %d\n",CLOCK_DIVIDER_B,CLOCK_DIVIDER_A,(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A,v,CLOCK_DIVIDER_N);
365-
//Serial.printf("freq %f %f\n",freq,I2S_BASE_CLK/(CLOCK_DIVIDER_N+(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A));
370+
//printf("freq %f %f\n",freq,I2S_BASE_CLK/(CLOCK_DIVIDER_N+(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A));
366371
freq=1/(CLOCK_DIVIDER_N+(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A);
367372
freq=freq*I2S_BASE_CLK;
368-
// 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);
373+
// 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);
369374
// double pulseduration=1000000000/freq;
370-
// Serial.printf("Pulse duration: %f ns\n",pulseduration);
375+
// printf("Pulse duration: %f ns\n",pulseduration);
371376
// gPulsesPerBit = (T1ns + T2ns + T3ns)/FASTLED_I2S_NS_PER_PULSE;
372377

373-
//Serial.print("Pulses per bit: "); Serial.println(gPulsesPerBit);
378+
//print("Pulses per bit: "); println(gPulsesPerBit);
374379

375380
//int ones_for_one = ((T1ns + T2ns - 1)/FASTLED_I2S_NS_PER_PULSE) + 1;
376381
ones_for_one = T1/pgc_ +T2/pgc_;
377-
//Serial.print("One bit: target ");
378-
//Serial.print(T1ns+T2ns); Serial.print("ns --- ");
379-
//Serial.print(ones_for_one); Serial.print(" 1 bits");
380-
//Serial.print(" = "); Serial.print(ones_for_one * FASTLED_I2S_NS_PER_PULSE); Serial.println("ns");
381-
// Serial.printf("one bit : target %d ns --- %d pulses 1 bit = %f ns\n",T1ns+T2ns,ones_for_one ,ones_for_one*pulseduration);
382+
//print("One bit: target ");
383+
//print(T1ns+T2ns); print("ns --- ");
384+
//print(ones_for_one); print(" 1 bits");
385+
//print(" = "); print(ones_for_one * FASTLED_I2S_NS_PER_PULSE); println("ns");
386+
// printf("one bit : target %d ns --- %d pulses 1 bit = %f ns\n",T1ns+T2ns,ones_for_one ,ones_for_one*pulseduration);
382387

383388

384389
int i = 0;
@@ -393,11 +398,11 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
393398

394399
//int ones_for_zero = ((T1ns - 1)/FASTLED_I2S_NS_PER_PULSE) + 1;
395400
ones_for_zero =T1/pgc_ ;
396-
// Serial.print("Zero bit: target ");
397-
// Serial.print(T1ns); Serial.print("ns --- ");
398-
//Serial.print(ones_for_zero); Serial.print(" 1 bits");
399-
//Serial.print(" = "); Serial.print(ones_for_zero * FASTLED_I2S_NS_PER_PULSE); Serial.println("ns");
400-
// Serial.printf("Zero bit : target %d ns --- %d pulses 1 bit = %f ns\n",T1ns,ones_for_zero ,ones_for_zero*pulseduration);
401+
// print("Zero bit: target ");
402+
// print(T1ns); print("ns --- ");
403+
//print(ones_for_zero); print(" 1 bits");
404+
//print(" = "); print(ones_for_zero * FASTLED_I2S_NS_PER_PULSE); println("ns");
405+
// printf("Zero bit : target %d ns --- %d pulses 1 bit = %f ns\n",T1ns,ones_for_zero ,ones_for_zero*pulseduration);
401406
i = 0;
402407
while ( i < ones_for_zero ) {
403408
gZeroBit[i] = 0xFFFFFF00;
@@ -513,16 +518,18 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
513518

514519
// -- Allocate i2s interrupt
515520
SET_PERI_REG_BITS(I2S_INT_ENA_REG(I2S_DEVICE), I2S_OUT_EOF_INT_ENA_V, 1, I2S_OUT_EOF_INT_ENA_S);
516-
esp_intr_alloc(interruptSource, 0, // ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3,
517-
&interruptHandler, 0, &gI2S_intr_handle);
521+
ESP_ERROR_CHECK(
522+
esp_intr_alloc(interruptSource, 0 /* ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3*/,
523+
&interruptHandler, 0, &gI2S_intr_handle)
524+
);
518525

519526
// -- Create a semaphore to block execution until all the controllers are done
520527
if (gTX_sem == NULL) {
521528
gTX_sem = xSemaphoreCreateBinary();
522529
xSemaphoreGive(gTX_sem);
523530
}
524531

525-
// Serial.println("Init I2S");
532+
// println("Init I2S");
526533
gInitialized = true;
527534
}
528535

@@ -562,8 +569,8 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
562569
// -- Keep track of the number of strips we've seen
563570
gNumStarted++;
564571

565-
// Serial.print("Show pixels ");
566-
// Serial.println(gNumStarted);
572+
// print("Show pixels ");
573+
// println(gNumStarted);
567574

568575
// -- The last call to showPixels is the one responsible for doing
569576
// all of the actual work
@@ -619,7 +626,7 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
619626
* from each strip), transpose and encode the bits, and store
620627
* them in the DMA buffer for the I2S peripheral to read.
621628
*/
622-
static void fillBuffer()
629+
static IRAM_ATTR void fillBuffer()
623630
{
624631
// -- Alternate between buffers
625632
volatile uint32_t * buf = (uint32_t *) dmaBuffers[gCurBuffer]->buffer;
@@ -659,7 +666,7 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
659666
// -- Tranpose each array: all the bit 7's, then all the bit 6's, ...
660667
transpose32(gPixelRow[channel], gPixelBits[channel][0] );
661668

662-
//Serial.print("Channel: "); Serial.print(channel); Serial.print(" ");
669+
//print("Channel: "); print(channel); print(" ");
663670
for (int bitnum = 0; bitnum < 8; bitnum++) {
664671
uint8_t * row = (uint8_t *) (gPixelBits[channel][bitnum]);
665672
uint32_t bit = (row[0] << 24) | (row[1] << 16) | (row[2] << 8) | row[3];
@@ -717,9 +724,9 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
717724
static void i2sStart()
718725
{
719726
// esp_intr_disable(gI2S_intr_handle);
720-
// Serial.println("I2S start");
727+
// println("I2S start");
721728
i2sReset();
722-
//Serial.println(dmaBuffers[0]->sampleCount());
729+
//println(dmaBuffers[0]->sampleCount());
723730
i2s->lc_conf.val=I2S_OUT_DATA_BURST_EN | I2S_OUTDSCR_BURST_EN | I2S_OUT_DATA_BURST_EN;
724731
i2s->out_link.addr = (uint32_t) & (dmaBuffers[0]->descriptor);
725732
i2s->out_link.start = 1;
@@ -740,7 +747,7 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
740747

741748
static void i2sReset()
742749
{
743-
// Serial.println("I2S reset");
750+
// println("I2S reset");
744751
const unsigned long lc_conf_reset_flags = I2S_IN_RST_M | I2S_OUT_RST_M | I2S_AHBM_RST_M | I2S_AHBM_FIFO_RST_M;
745752
i2s->lc_conf.val |= lc_conf_reset_flags;
746753
i2s->lc_conf.val &= ~lc_conf_reset_flags;
@@ -764,7 +771,7 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
764771

765772
static void i2sStop()
766773
{
767-
// Serial.println("I2S stop");
774+
// println("I2S stop");
768775
esp_intr_disable(gI2S_intr_handle);
769776
i2sReset();
770777
i2s->conf.rx_start = 0;

components/FastLED-idf/platforms/esp/32/fastled_esp32.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
#include "fastpin_esp32.h"
44

5+
#ifdef FASTLED_ALL_PINS_HARDWARE_SPI
6+
#include "fastspi_esp32.h"
7+
#endif
8+
59
#ifdef FASTLED_ESP32_I2S
610
#include "clockless_i2s_esp32.h"
711
#else

0 commit comments

Comments
 (0)