kopia lustrzana https://github.com/Aircoookie/WLED
				
				
				
			Dynamic parallel I2S output
- update NeoPixelBus to v2.8.0 - use single/mono I2S + 4x RMT for 5 outputs or less - use parallel x8 I2S + 8x RMT for >5 outputs (limit of 300 LEDs per output)pull/4030/head
							rodzic
							
								
									25ade86994
								
							
						
					
					
						commit
						f4475b9d2a
					
				| 
						 | 
				
			
			@ -138,7 +138,8 @@ lib_compat_mode = strict
 | 
			
		|||
lib_deps =
 | 
			
		||||
    fastled/FastLED @ 3.6.0
 | 
			
		||||
    IRremoteESP8266 @ 2.8.2
 | 
			
		||||
    makuna/NeoPixelBus @ 2.7.9
 | 
			
		||||
    makuna/NeoPixelBus @ 2.8.0
 | 
			
		||||
    #https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta
 | 
			
		||||
    https://github.com/Aircoookie/ESPAsyncWebServer.git @ 2.2.1
 | 
			
		||||
  # for I2C interface
 | 
			
		||||
    ;Wire
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2103,7 +2103,7 @@ uint16_t mode_fire_2012() {
 | 
			
		|||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  for (int stripNr=0; stripNr<strips; stripNr++)
 | 
			
		||||
  for (unsigned stripNr=0; stripNr<strips; stripNr++)
 | 
			
		||||
    virtualStrip::runStrip(stripNr, &heat[stripNr * SEGLEN], it);
 | 
			
		||||
 | 
			
		||||
  if (SEGMENT.is2D()) SEGMENT.blur(32);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1704,8 +1704,6 @@ void WS2812FX::printSize() {
 | 
			
		|||
  DEBUG_PRINTF_P(PSTR("Modes: %d*%d=%uB\n"), sizeof(mode_ptr), _mode.size(), (_mode.capacity()*sizeof(mode_ptr)));
 | 
			
		||||
  DEBUG_PRINTF_P(PSTR("Data: %d*%d=%uB\n"), sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *)));
 | 
			
		||||
  DEBUG_PRINTF_P(PSTR("Map: %d*%d=%uB\n"), sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t));
 | 
			
		||||
  size = getLengthTotal();
 | 
			
		||||
  if (useGlobalLedBuffer) DEBUG_PRINTF_P(PSTR("Buffer: %d*%u=%uB\n"), sizeof(CRGB), size, size*sizeof(CRGB));
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -685,11 +685,11 @@ uint32_t BusManager::memUsage(BusConfig &bc) {
 | 
			
		|||
      if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
 | 
			
		||||
        multiplier = 5;
 | 
			
		||||
      }
 | 
			
		||||
    #else //ESP32 RMT uses double buffer, I2S uses 5x buffer
 | 
			
		||||
      multiplier = 2;
 | 
			
		||||
    #else //ESP32 RMT uses double buffer, parallel I2S uses 8x buffer (3 times)
 | 
			
		||||
      multiplier = PolyBus::isParallelI2S1Output() ? 24 : 2;
 | 
			
		||||
    #endif
 | 
			
		||||
  }
 | 
			
		||||
  return len * channels * multiplier; //RGB
 | 
			
		||||
  return (len * multiplier + bc.doubleBuffer * (bc.count + bc.skipAmount)) * channels;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int BusManager::add(BusConfig &bc) {
 | 
			
		||||
| 
						 | 
				
			
			@ -706,6 +706,10 @@ int BusManager::add(BusConfig &bc) {
 | 
			
		|||
  return numBusses++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BusManager::useParallelOutput(void) {
 | 
			
		||||
  PolyBus::setParallelI2S1Output();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//do not call this method from system context (network callback)
 | 
			
		||||
void BusManager::removeAll() {
 | 
			
		||||
  DEBUG_PRINTLN(F("Removing all."));
 | 
			
		||||
| 
						 | 
				
			
			@ -713,6 +717,7 @@ void BusManager::removeAll() {
 | 
			
		|||
  while (!canAllShow()) yield();
 | 
			
		||||
  for (unsigned i = 0; i < numBusses; i++) delete busses[i];
 | 
			
		||||
  numBusses = 0;
 | 
			
		||||
  PolyBus::setParallelI2S1Output(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BusManager::show() {
 | 
			
		||||
| 
						 | 
				
			
			@ -781,6 +786,8 @@ uint16_t BusManager::getTotalLength() {
 | 
			
		|||
  return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PolyBus::useParallelI2S = false;
 | 
			
		||||
 | 
			
		||||
// Bus static member definition
 | 
			
		||||
int16_t Bus::_cct = -1;
 | 
			
		||||
uint8_t Bus::_cctBlend = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,10 +21,6 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb);
 | 
			
		|||
#define IC_INDEX_WS2812_2CH_3X(i)  ((i)*2/3)
 | 
			
		||||
#define WS2812_2CH_3X_SPANS_2_ICS(i) ((i)&0x01)    // every other LED zone is on two different ICs
 | 
			
		||||
 | 
			
		||||
// flag for using double buffering in BusDigital
 | 
			
		||||
extern bool useGlobalLedBuffer;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//temporary struct for passing bus configuration to bus
 | 
			
		||||
struct BusConfig {
 | 
			
		||||
  uint8_t type;
 | 
			
		||||
| 
						 | 
				
			
			@ -363,6 +359,7 @@ class BusManager {
 | 
			
		|||
    static uint16_t ablMilliampsMax(void)  { return _milliAmpsMax; }
 | 
			
		||||
 | 
			
		||||
    static int add(BusConfig &bc);
 | 
			
		||||
    static void useParallelOutput(void); // workaround for inaccessible PolyBus
 | 
			
		||||
 | 
			
		||||
    //do not call this method from system context (network callback)
 | 
			
		||||
    static void removeAll();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												Plik diff jest za duży
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -156,18 +156,42 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
 | 
			
		|||
  JsonArray ins = hw_led["ins"];
 | 
			
		||||
 | 
			
		||||
  if (fromFS || !ins.isNull()) {
 | 
			
		||||
    DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), ESP.getFreeHeap());
 | 
			
		||||
    int s = 0;  // bus iterator
 | 
			
		||||
    if (fromFS) BusManager::removeAll(); // can't safely manipulate busses directly in network callback
 | 
			
		||||
    uint32_t mem = 0, globalBufMem = 0;
 | 
			
		||||
    uint16_t maxlen = 0;
 | 
			
		||||
    uint32_t mem = 0;
 | 
			
		||||
    bool busesChanged = false;
 | 
			
		||||
    // determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
 | 
			
		||||
    bool useParallel = false;
 | 
			
		||||
    #if defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP32S2) && !defined(ARDUINO_ARCH_ESP32S3) && !defined(ARDUINO_ARCH_ESP32C3)
 | 
			
		||||
    unsigned digitalCount = 0;
 | 
			
		||||
    unsigned maxLeds = 0;
 | 
			
		||||
    int oldType = 0;
 | 
			
		||||
    int j = 0;
 | 
			
		||||
    for (JsonObject elm : ins) {
 | 
			
		||||
      unsigned type = elm["type"] | TYPE_WS2812_RGB;
 | 
			
		||||
      unsigned len = elm["len"] | 30;
 | 
			
		||||
      if (IS_DIGITAL(type) && !IS_2PIN(type)) digitalCount++;
 | 
			
		||||
      if (len > maxLeds) maxLeds = len;
 | 
			
		||||
      // we need to have all LEDs of the same type for parallel
 | 
			
		||||
      if (j++ < 8 && oldType > 0 && oldType != type) oldType = -1;
 | 
			
		||||
      else if (oldType == 0) oldType = type;
 | 
			
		||||
    }
 | 
			
		||||
    DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\nDifferent types: %d\n"), maxLeds, digitalCount, (int)(oldType == -1));
 | 
			
		||||
    // we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
 | 
			
		||||
    if (/*oldType != -1 && */maxLeds <= 300 && digitalCount > 5) {
 | 
			
		||||
      useParallel = true;
 | 
			
		||||
      BusManager::useParallelOutput();
 | 
			
		||||
      DEBUG_PRINTF_P(PSTR("Switching to parallel I2S with max. %d LEDs per ouptut.\n"), maxLeds);
 | 
			
		||||
    }
 | 
			
		||||
    #endif
 | 
			
		||||
    for (JsonObject elm : ins) {
 | 
			
		||||
      if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break;
 | 
			
		||||
      uint8_t pins[5] = {255, 255, 255, 255, 255};
 | 
			
		||||
      JsonArray pinArr = elm["pin"];
 | 
			
		||||
      if (pinArr.size() == 0) continue;
 | 
			
		||||
      pins[0] = pinArr[0];
 | 
			
		||||
      uint8_t i = 0;
 | 
			
		||||
      unsigned i = 0;
 | 
			
		||||
      for (int p : pinArr) {
 | 
			
		||||
        pins[i++] = p;
 | 
			
		||||
        if (i>4) break;
 | 
			
		||||
| 
						 | 
				
			
			@ -193,12 +217,16 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
 | 
			
		|||
      ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
 | 
			
		||||
      if (fromFS) {
 | 
			
		||||
        BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
 | 
			
		||||
        mem += BusManager::memUsage(bc);
 | 
			
		||||
        if (useGlobalLedBuffer && start + length > maxlen) {
 | 
			
		||||
          maxlen = start + length;
 | 
			
		||||
          globalBufMem = maxlen * 4;
 | 
			
		||||
        }
 | 
			
		||||
        if (mem + globalBufMem <= MAX_LED_MEMORY) if (BusManager::add(bc) == -1) break;  // finalization will be done in WLED::beginStrip()
 | 
			
		||||
        if (useParallel && s < 8) {
 | 
			
		||||
          // we are using parallel I2S and memUsage() will include x8 allocation into account
 | 
			
		||||
          if (s == 0)
 | 
			
		||||
            mem = BusManager::memUsage(bc); // includes x8 memory allocation for parallel I2S
 | 
			
		||||
          else
 | 
			
		||||
            if (BusManager::memUsage(bc) > mem)
 | 
			
		||||
              mem = BusManager::memUsage(bc); // if we have unequal LED count use the largest
 | 
			
		||||
        } else
 | 
			
		||||
          mem += BusManager::memUsage(bc); // includes global buffer
 | 
			
		||||
        if (mem <= MAX_LED_MEMORY) if (BusManager::add(bc) == -1) break;  // finalization will be done in WLED::beginStrip()
 | 
			
		||||
      } else {
 | 
			
		||||
        if (busConfigs[s] != nullptr) delete busConfigs[s];
 | 
			
		||||
        busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
 | 
			
		||||
| 
						 | 
				
			
			@ -206,6 +234,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
 | 
			
		|||
      }
 | 
			
		||||
      s++;
 | 
			
		||||
    }
 | 
			
		||||
    DEBUG_PRINTF_P(PSTR("LED buffer size: %uB\n"), mem);
 | 
			
		||||
    DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap());
 | 
			
		||||
    doInitBusses = busesChanged;
 | 
			
		||||
    // finalization done in beginStrip()
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -215,7 +245,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
 | 
			
		|||
  JsonArray hw_com = hw[F("com")];
 | 
			
		||||
  if (!hw_com.isNull()) {
 | 
			
		||||
    ColorOrderMap com = {};
 | 
			
		||||
    uint8_t s = 0;
 | 
			
		||||
    unsigned s = 0;
 | 
			
		||||
    for (JsonObject entry : hw_com) {
 | 
			
		||||
      if (s > WLED_MAX_COLOR_ORDER_MAPPINGS) break;
 | 
			
		||||
      uint16_t start = entry["start"] | 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -234,10 +264,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
 | 
			
		|||
  disablePullUp = !pull;
 | 
			
		||||
  JsonArray hw_btn_ins = btn_obj["ins"];
 | 
			
		||||
  if (!hw_btn_ins.isNull()) {
 | 
			
		||||
    for (uint8_t b = 0; b < WLED_MAX_BUTTONS; b++) { // deallocate existing button pins
 | 
			
		||||
      pinManager.deallocatePin(btnPin[b], PinOwner::Button); // does nothing if trying to deallocate a pin with PinOwner != Button
 | 
			
		||||
    }
 | 
			
		||||
    uint8_t s = 0;
 | 
			
		||||
    // deallocate existing button pins
 | 
			
		||||
    for (unsigned b = 0; b < WLED_MAX_BUTTONS; b++) pinManager.deallocatePin(btnPin[b], PinOwner::Button); // does nothing if trying to deallocate a pin with PinOwner != Button
 | 
			
		||||
    unsigned s = 0;
 | 
			
		||||
    for (JsonObject btn : hw_btn_ins) {
 | 
			
		||||
      CJSON(buttonType[s], btn["type"]);
 | 
			
		||||
      int8_t pin = btn["pin"][0] | -1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,11 +61,7 @@
 | 
			
		|||
      #define WLED_MIN_VIRTUAL_BUSSES 4
 | 
			
		||||
    #else
 | 
			
		||||
      // the last digital bus (I2S0) will prevent Audioreactive usermod from functioning
 | 
			
		||||
      #ifndef WLED_USE_PARALLEL_I2S
 | 
			
		||||
      #define WLED_MAX_BUSSES 10
 | 
			
		||||
      #else
 | 
			
		||||
      #define WLED_MAX_BUSSES 17
 | 
			
		||||
      #endif
 | 
			
		||||
      #define WLED_MIN_VIRTUAL_BUSSES 0
 | 
			
		||||
    #endif
 | 
			
		||||
  #endif
 | 
			
		||||
| 
						 | 
				
			
			@ -76,10 +72,10 @@
 | 
			
		|||
    #endif
 | 
			
		||||
    #define WLED_MIN_VIRTUAL_BUSSES (5-WLED_MAX_BUSSES)
 | 
			
		||||
  #else
 | 
			
		||||
    #if WLED_MAX_BUSES > 10
 | 
			
		||||
      #error Maximum number of buses is 10.
 | 
			
		||||
    #if WLED_MAX_BUSES > 17
 | 
			
		||||
      #error Maximum number of buses is 17.
 | 
			
		||||
    #endif
 | 
			
		||||
    #define WLED_MIN_VIRTUAL_BUSSES (10-WLED_MAX_BUSSES)
 | 
			
		||||
    #define WLED_MIN_VIRTUAL_BUSSES (17-WLED_MAX_BUSSES)
 | 
			
		||||
  #endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -175,19 +175,44 @@ void WLED::loop()
 | 
			
		|||
    DEBUG_PRINTLN(F("Re-init busses."));
 | 
			
		||||
    bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses)
 | 
			
		||||
    BusManager::removeAll();
 | 
			
		||||
    uint32_t mem = 0, globalBufMem = 0;
 | 
			
		||||
    uint16_t maxlen = 0;
 | 
			
		||||
    for (uint8_t i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
 | 
			
		||||
    // determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
 | 
			
		||||
    bool useParallel = false;
 | 
			
		||||
    #if defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP32S2) && !defined(ARDUINO_ARCH_ESP32S3) && !defined(ARDUINO_ARCH_ESP32C3)
 | 
			
		||||
    unsigned digitalCount = 0;
 | 
			
		||||
    unsigned maxLeds = 0;
 | 
			
		||||
    int oldType = 0;
 | 
			
		||||
    for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
 | 
			
		||||
      if (busConfigs[i] == nullptr) break;
 | 
			
		||||
      mem += BusManager::memUsage(*busConfigs[i]);
 | 
			
		||||
      if (useGlobalLedBuffer && busConfigs[i]->start + busConfigs[i]->count > maxlen) {
 | 
			
		||||
          maxlen = busConfigs[i]->start + busConfigs[i]->count;
 | 
			
		||||
          globalBufMem = maxlen * 4;
 | 
			
		||||
      }
 | 
			
		||||
      if (mem + globalBufMem <= MAX_LED_MEMORY) {
 | 
			
		||||
        BusManager::add(*busConfigs[i]);
 | 
			
		||||
      }
 | 
			
		||||
      delete busConfigs[i]; busConfigs[i] = nullptr;
 | 
			
		||||
      if (IS_DIGITAL(busConfigs[i]->type) && !IS_2PIN(busConfigs[i]->type)) digitalCount++;
 | 
			
		||||
      if (busConfigs[i]->count > maxLeds) maxLeds = busConfigs[i]->count;
 | 
			
		||||
      // we need to have all LEDs of the same type for parallel
 | 
			
		||||
      if (i < 8 && oldType > 0 && oldType != busConfigs[i]->type) oldType = -1;
 | 
			
		||||
      else if (oldType == 0) oldType = busConfigs[i]->type;
 | 
			
		||||
    }
 | 
			
		||||
    DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\nDifferent types: %d\n"), maxLeds, digitalCount, (int)(oldType == -1));
 | 
			
		||||
    // we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
 | 
			
		||||
    if (/*oldType != -1 && */maxLeds <= 300 && digitalCount > 5) {
 | 
			
		||||
      useParallel = true;
 | 
			
		||||
      BusManager::useParallelOutput();
 | 
			
		||||
      DEBUG_PRINTF_P(PSTR("Switching to parallel I2S with max. %d LEDs per ouptut.\n"), maxLeds);
 | 
			
		||||
    }
 | 
			
		||||
    #endif
 | 
			
		||||
    // create buses/outputs
 | 
			
		||||
    unsigned mem = 0;
 | 
			
		||||
    for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
 | 
			
		||||
      if (busConfigs[i] == nullptr || (!useParallel && i > 10)) break;
 | 
			
		||||
      if (useParallel && i < 8) {
 | 
			
		||||
        // we are using parallel I2S and memUsage() will include x8 allocation into account
 | 
			
		||||
        if (i == 0)
 | 
			
		||||
          mem = BusManager::memUsage(*busConfigs[i]); // includes x8 memory allocation for parallel I2S
 | 
			
		||||
        else
 | 
			
		||||
          if (BusManager::memUsage(*busConfigs[i]) > mem)
 | 
			
		||||
            mem = BusManager::memUsage(*busConfigs[i]); // if we have unequal LED count use the largest
 | 
			
		||||
      } else
 | 
			
		||||
        mem += BusManager::memUsage(*busConfigs[i]); // includes global buffer
 | 
			
		||||
      if (mem <= MAX_LED_MEMORY) BusManager::add(*busConfigs[i]);
 | 
			
		||||
      delete busConfigs[i];
 | 
			
		||||
      busConfigs[i] = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    strip.finalizeInit(); // also loads default ledmap if present
 | 
			
		||||
    if (aligned) strip.makeAutoSegments();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
// version code in format yymmddb (b = daily build)
 | 
			
		||||
#define VERSION 2405180
 | 
			
		||||
#define VERSION 2406120
 | 
			
		||||
 | 
			
		||||
//uncomment this if you have a "my_config.h" file you'd like to use
 | 
			
		||||
//#define WLED_USE_MY_CONFIG
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue