Skip to content

Commit

Permalink
Merge pull request #4484 from blazoncek/parallel-I2S
Browse files Browse the repository at this point in the history
WWA strip support & parallel I2S for S2/S3 (bumping outputs from 5/4 to 12)
  • Loading branch information
blazoncek authored Feb 13, 2025
2 parents 386e2c6 + 77d7082 commit a0c55c6
Show file tree
Hide file tree
Showing 15 changed files with 815 additions and 675 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ wled-update.sh

/build_output/
/node_modules/
/logs/

/wled00/extLibs
/wled00/LittleFS
Expand Down
2 changes: 1 addition & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ lib_compat_mode = strict
lib_deps =
fastled/FastLED @ 3.6.0
IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.8.0
makuna/NeoPixelBus @ 2.8.3
#https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.0
# for I2C interface
Expand Down
22 changes: 13 additions & 9 deletions wled00/FX.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#pragma once
/*
WS2812FX.h - Library for WS2812 LED effects.
Harm Aldick - 2016
Expand All @@ -8,12 +9,15 @@
Adapted from code originally licensed under the MIT license
Modified for WLED
Segment class/struct (c) 2022 Blaz Kristan (@blazoncek)
*/

#ifndef WS2812FX_h
#define WS2812FX_h

#include <vector>
#include "wled.h"

#include "const.h"
#include "bus_manager.h"
Expand Down Expand Up @@ -71,18 +75,15 @@ extern byte realtimeMode; // used in getMappedPixelIndex()
/* each segment uses 82 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
#ifdef ESP8266
#define MAX_NUM_SEGMENTS 16
#define MAX_NUM_SEGMENTS 16
/* How much data bytes all segments combined may allocate */
#define MAX_SEGMENT_DATA 5120
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
#define MAX_NUM_SEGMENTS 20
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*512) // 10k by default (S2 is short on free RAM)
#else
#ifndef MAX_NUM_SEGMENTS
#define MAX_NUM_SEGMENTS 32
#endif
#if defined(ARDUINO_ARCH_ESP32S2)
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*768) // 24k by default (S2 is short on free RAM)
#else
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*1280) // 40k by default
#endif
#define MAX_NUM_SEGMENTS 32 // warning: going beyond 32 may consume too much RAM for stable operation
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*1280) // 40k by default
#endif

/* How much data bytes each segment should max allocate to leave enough space for other segments,
Expand Down Expand Up @@ -571,6 +572,8 @@ typedef struct Segment {
inline uint16_t groupLength() const { return grouping + spacing; }
inline uint8_t getLightCapabilities() const { return _capabilities; }
inline void deactivate() { setGeometry(0,0); }
inline Segment &clearName() { if (name) free(name); name = nullptr; return *this; }
inline Segment &setName(const String &name) { return setName(name.c_str()); }

inline static unsigned getUsedSegmentData() { return Segment::_usedSegmentData; }
inline static void addUsedSegmentData(int len) { Segment::_usedSegmentData += len; }
Expand All @@ -593,6 +596,7 @@ typedef struct Segment {
Segment &setOption(uint8_t n, bool val);
Segment &setMode(uint8_t fx, bool loadDefaults = false);
Segment &setPalette(uint8_t pal);
Segment &setName(const char* name);
uint8_t differs(const Segment& b) const;
void refreshLightCapabilities();

Expand Down
46 changes: 46 additions & 0 deletions wled00/FX_fcn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,20 @@ Segment &Segment::setPalette(uint8_t pal) {
return *this;
}

Segment &Segment::setName(const char *newName) {
if (newName) {
const int newLen = min(strlen(newName), (size_t)WLED_MAX_SEGNAME_LEN);
if (newLen) {
if (name) name = static_cast<char*>(realloc(name, newLen+1));
else name = static_cast<char*>(malloc(newLen+1));
if (name) strlcpy(name, newName, newLen+1);
name[newLen] = 0;
return *this;
}
}
return clearName();
}

// 2D matrix
unsigned Segment::virtualWidth() const {
unsigned groupLen = groupLength();
Expand Down Expand Up @@ -1311,6 +1325,34 @@ void WS2812FX::finalizeInit() {

_hasWhiteChannel = _isOffRefreshRequired = false;

unsigned digitalCount = 0;
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
unsigned maxLedsOnBus = 0;
for (const auto &bus : busConfigs) {
if (Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type)) {
digitalCount++;
if (bus.count > maxLedsOnBus) maxLedsOnBus = bus.count;
}
}
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
// we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
if (maxLedsOnBus <= 300 && useParallelI2S) BusManager::useParallelOutput(); // must call before creating buses
else useParallelI2S = false; // enforce single I2S
#endif

// create buses/outputs
unsigned mem = 0;
digitalCount = 0;
for (const auto &bus : busConfigs) {
mem += bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount++ : 0); // includes global buffer
if (mem <= MAX_LED_MEMORY) {
if (BusManager::add(bus) == -1) break;
} else DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)bus.type, (int)bus.count, digitalCount);
}
busConfigs.clear();
busConfigs.shrink_to_fit();

//if busses failed to load, add default (fresh install, FS issue, ...)
if (BusManager::getNumBusses() == 0) {
DEBUG_PRINTLN(F("No busses, init default"));
Expand All @@ -1326,6 +1368,7 @@ void WS2812FX::finalizeInit() {

unsigned prevLen = 0;
unsigned pinsIndex = 0;
digitalCount = 0;
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
uint8_t defPin[OUTPUT_MAX_PINS];
// if we have less types than requested outputs and they do not align, use last known type to set current type
Expand Down Expand Up @@ -1390,9 +1433,11 @@ void WS2812FX::finalizeInit() {
if (Bus::isPWM(dataType) || Bus::isOnOff(dataType)) count = 1;
prevLen += count;
BusConfig defCfg = BusConfig(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0, useGlobalLedBuffer);
mem += defCfg.memUsage(Bus::isDigital(dataType) && !Bus::is2Pin(dataType) ? digitalCount++ : 0);
if (BusManager::add(defCfg) == -1) break;
}
}
DEBUG_PRINTF_P(PSTR("LED buffer size: %uB/%uB\n"), mem, BusManager::memUsage());

_length = 0;
for (int i=0; i<BusManager::getNumBusses(); i++) {
Expand All @@ -1409,6 +1454,7 @@ void WS2812FX::finalizeInit() {
// This must be done after all buses have been created, as some kinds (parallel I2S) interact
bus->begin();
}
DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap());

Segment::maxWidth = _length;
Segment::maxHeight = 1;
Expand Down
Loading

0 comments on commit a0c55c6

Please sign in to comment.