diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 0314321ae..795096c05 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -405,22 +405,23 @@ BusPwm::BusPwm(BusConfig &bc) // duty cycle resolution (_depth) can be extracted from this formula: CLOCK_FREQUENCY > _frequency * 2^_depth for (_depth = MAX_BIT_WIDTH; _depth > 8; _depth--) if (((CLOCK_FREQUENCY/_frequency) >> _depth) > 0) break; + managed_pin_type pins[numPins]; + for (unsigned i = 0; i < numPins; i++) pins[i] = {(int8_t)bc.pins[i], true}; + if (!pinManager.allocateMultiplePins(pins, numPins, PinOwner::BusPwm)) return; + #ifdef ESP8266 analogWriteRange((1<<_depth)-1); analogWriteFreq(_frequency); #else _ledcStart = pinManager.allocateLedc(numPins); if (_ledcStart == 255) { //no more free LEDC channels - deallocatePins(); return; + pinManager.deallocateMultiplePins(pins, numPins, PinOwner::BusPwm); + return; } #endif for (unsigned i = 0; i < numPins; i++) { - uint8_t currentPin = bc.pins[i]; - if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) { - deallocatePins(); return; - } - _pins[i] = currentPin; //store only after allocatePin() succeeds + _pins[i] = bc.pins[i]; // store only after allocateMultiplePins() succeeded #ifdef ESP8266 pinMode(_pins[i], OUTPUT); #else @@ -539,7 +540,7 @@ void BusPwm::deallocatePins(void) { #ifdef ESP8266 digitalWrite(_pins[i], LOW); //turn off PWM interrupt #else - if (_ledcStart < 16) ledcDetachPin(_pins[i]); + if (_ledcStart < WLED_MAX_ANALOG_CHANNELS) ledcDetachPin(_pins[i]); #endif } #ifdef ARDUINO_ARCH_ESP32 diff --git a/wled00/const.h b/wled00/const.h index a87c6ff30..bdc80aab9 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -51,27 +51,28 @@ #define WLED_MAX_BUSSES 4 // will allow 3 digital & 1 analog RGB #define WLED_MIN_VIRTUAL_BUSSES 2 #else + #define WLED_MAX_ANALOG_CHANNELS (LEDC_CHANNEL_MAX*LEDC_SPEED_MODE_MAX) #if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM #define WLED_MAX_BUSSES 4 // will allow 2 digital & 2 analog RGB #define WLED_MAX_DIGITAL_CHANNELS 2 - #define WLED_MAX_ANALOG_CHANNELS 6 + //#define WLED_MAX_ANALOG_CHANNELS 6 #define WLED_MIN_VIRTUAL_BUSSES 3 #elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB // the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though) #define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog RGB #define WLED_MAX_DIGITAL_CHANNELS 5 - #define WLED_MAX_ANALOG_CHANNELS 8 + //#define WLED_MAX_ANALOG_CHANNELS 8 #define WLED_MIN_VIRTUAL_BUSSES 3 #elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB does not support them ATM #define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog RGB #define WLED_MAX_DIGITAL_CHANNELS 4 - #define WLED_MAX_ANALOG_CHANNELS 8 + //#define WLED_MAX_ANALOG_CHANNELS 8 #define WLED_MIN_VIRTUAL_BUSSES 4 #else // the last digital bus (I2S0) will prevent Audioreactive usermod from functioning #define WLED_MAX_BUSSES 20 // will allow 17 digital & 3 analog RGB #define WLED_MAX_DIGITAL_CHANNELS 17 - #define WLED_MAX_ANALOG_CHANNELS 10 + //#define WLED_MAX_ANALOG_CHANNELS 16 #define WLED_MIN_VIRTUAL_BUSSES 4 #endif #endif diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 84101e7cf..0a0f39155 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -32,9 +32,7 @@ bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag) return false; } - byte by = gpio >> 3; - byte bi = gpio - 8*by; - bitWrite(pinAlloc[by], bi, false); + bitWrite(pinAlloc, gpio, false); ownerTag[gpio] = PinOwner::None; return true; } @@ -99,7 +97,7 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by bool shouldFail = false; // first verify the pins are OK and not already allocated for (int i = 0; i < arrayElementCount; i++) { - byte gpio = mptArray[i].pin; + unsigned gpio = mptArray[i].pin; if (gpio == 0xFF) { // explicit support for io -1 as a no-op (no allocation of pin), // as this can greatly simplify configuration arrays @@ -137,7 +135,7 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by // all pins are available .. track each one for (int i = 0; i < arrayElementCount; i++) { - byte gpio = mptArray[i].pin; + unsigned gpio = mptArray[i].pin; if (gpio == 0xFF) { // allow callers to include -1 value as non-requested pin // as this can greatly simplify configuration arrays @@ -146,9 +144,7 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by if (gpio >= WLED_NUM_PINS) continue; // other unexpected GPIO => avoid array bounds violation - byte by = gpio >> 3; - byte bi = gpio - 8*by; - bitWrite(pinAlloc[by], bi, true); + bitWrite(pinAlloc, gpio, true); ownerTag[gpio] = tag; #ifdef WLED_DEBUG DEBUG_PRINT(F("PIN ALLOC: Pin ")); @@ -192,9 +188,7 @@ bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag) return false; } - byte by = gpio >> 3; - byte bi = gpio - 8*by; - bitWrite(pinAlloc[by], bi, true); + bitWrite(pinAlloc, gpio, true); ownerTag[gpio] = tag; #ifdef WLED_DEBUG DEBUG_PRINT(F("PIN ALLOC: Pin ")); @@ -213,9 +207,7 @@ bool PinManagerClass::isPinAllocated(byte gpio, PinOwner tag) const { if (!isPinOk(gpio, false)) return true; if ((tag != PinOwner::None) && (ownerTag[gpio] != tag)) return false; - byte by = gpio >> 3; - byte bi = gpio - (by<<3); - return bitRead(pinAlloc[by], bi); + return bitRead(pinAlloc, gpio); } /* see https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/gpio.html @@ -237,7 +229,7 @@ bool PinManagerClass::isPinAllocated(byte gpio, PinOwner tag) const // Check if supplied GPIO is ok to use bool PinManagerClass::isPinOk(byte gpio, bool output) const { - if (gpio >= WLED_NUM_PINS) return false; // catch error case, to avoid array out-of-bounds access + if (gpio >= WLED_NUM_PINS) return false; // catch error case, to avoid array out-of-bounds access #ifdef ARDUINO_ARCH_ESP32 if (digitalPinIsValid(gpio)) { #if defined(CONFIG_IDF_TARGET_ESP32C3) @@ -282,34 +274,26 @@ PinOwner PinManagerClass::getPinOwner(byte gpio) const } #ifdef ARDUINO_ARCH_ESP32 -#if defined(CONFIG_IDF_TARGET_ESP32C3) - #define MAX_LED_CHANNELS 6 -#else - #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) - #define MAX_LED_CHANNELS 8 - #else - #define MAX_LED_CHANNELS 16 - #endif -#endif byte PinManagerClass::allocateLedc(byte channels) { - if (channels > MAX_LED_CHANNELS || channels == 0) return 255; - byte ca = 0; - for (unsigned i = 0; i < MAX_LED_CHANNELS; i++) { - byte by = i >> 3; - byte bi = i - 8*by; - if (bitRead(ledcAlloc[by], bi)) { //found occupied pin + if (channels > WLED_MAX_ANALOG_CHANNELS || channels == 0) return 255; + unsigned ca = 0; + for (unsigned i = 0; i < WLED_MAX_ANALOG_CHANNELS; i++) { + if (bitRead(ledcAlloc, i)) { //found occupied pin ca = 0; } else { - ca++; + // if we have PWM CCT bus allocation (2 channels) we need to make sure both channels share the same timer + // for phase shifting purposes (otherwise phase shifts may not be accurate) + if (channels == 2) { // will skip odd channel for first channel for phase shifting + if (ca == 0 && i % 2 == 0) ca++; // even LEDC channels is 1st PWM channel + if (ca == 1 && i % 2 == 1) ca++; // odd LEDC channel is 2nd PWM channel + } else + ca++; } if (ca >= channels) { //enough free channels - byte in = (i + 1) - ca; + unsigned in = (i + 1) - ca; for (unsigned j = 0; j < ca; j++) { - byte bChan = in + j; - byte byChan = bChan >> 3; - byte biChan = bChan - 8*byChan; - bitWrite(ledcAlloc[byChan], biChan, true); + bitWrite(ledcAlloc, in+j, true); } return in; } @@ -319,11 +303,8 @@ byte PinManagerClass::allocateLedc(byte channels) void PinManagerClass::deallocateLedc(byte pos, byte channels) { - for (unsigned j = pos; j < pos + channels; j++) { - if (j > MAX_LED_CHANNELS) return; - byte by = j >> 3; - byte bi = j - 8*by; - bitWrite(ledcAlloc[by], bi, false); + for (unsigned j = pos; j < pos + channels && j < WLED_MAX_ANALOG_CHANNELS; j++) { + bitWrite(ledcAlloc, j, false); } } #endif diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index 2fc003880..986964a7f 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -4,6 +4,9 @@ * Registers pins so there is no attempt for two interfaces to use the same pin */ #include +#ifdef ARDUINO_ARCH_ESP32 +#include "driver/ledc.h" // needed for analog/LEDC channel counts +#endif #include "const.h" // for USERMOD_* values typedef struct PinManagerPinType { @@ -46,7 +49,6 @@ enum struct PinOwner : uint8_t { UM_RotaryEncoderUI = USERMOD_ID_ROTARY_ENC_UI, // 0x08 // Usermod "usermod_v2_rotary_encoder_ui.h" // #define USERMOD_ID_AUTO_SAVE // 0x09 // Usermod "usermod_v2_auto_save.h" -- Does not allocate pins // #define USERMOD_ID_DHT // 0x0A // Usermod "usermod_dht.h" -- Statically allocates pins, not compatible with pinManager? - // #define USERMOD_ID_MODE_SORT // 0x0B // Usermod "usermod_v2_mode_sort.h" -- Does not allocate pins // #define USERMOD_ID_VL53L0X // 0x0C // Usermod "usermod_vl53l0x_gestures.h" -- Uses "standard" HW_I2C pins UM_MultiRelay = USERMOD_ID_MULTI_RELAY, // 0x0D // Usermod "usermod_multi_relay.h" UM_AnimatedStaircase = USERMOD_ID_ANIMATED_STAIRCASE, // 0x0E // Usermod "Animated_Staircase.h" @@ -64,29 +66,28 @@ enum struct PinOwner : uint8_t { UM_LDR_DUSK_DAWN = USERMOD_ID_LDR_DUSK_DAWN, // 0x2B // Usermod "usermod_LDR_Dusk_Dawn_v2.h" UM_MAX17048 = USERMOD_ID_MAX17048, // 0x2F // Usermod "usermod_max17048.h" UM_BME68X = USERMOD_ID_BME68X, // 0x31 // Usermod "usermod_bme68x.h -- Uses "standard" HW_I2C pins - UM_PIXELS_DICE_TRAY = USERMOD_ID_PIXELS_DICE_TRAY, // 0x35 // Usermod "pixels_dice_tray.h" -- Needs compile time specified 6 pins for display including SPI. + UM_PIXELS_DICE_TRAY = USERMOD_ID_PIXELS_DICE_TRAY // 0x35 // Usermod "pixels_dice_tray.h" -- Needs compile time specified 6 pins for display including SPI. }; static_assert(0u == static_cast(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); class PinManagerClass { private: - #ifdef ESP8266 - #define WLED_NUM_PINS 17 - uint8_t pinAlloc[3] = {0x00, 0x00, 0x00}; //24bit, 1 bit per pin, we use first 17bits - PinOwner ownerTag[WLED_NUM_PINS] = { PinOwner::None }; - #else - #define WLED_NUM_PINS 50 - uint8_t pinAlloc[7] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 56bit, 1 bit per pin, we use 50 bits on ESP32-S3 - uint8_t ledcAlloc[2] = {0x00, 0x00}; //16 LEDC channels - PinOwner ownerTag[WLED_NUM_PINS] = { PinOwner::None }; // new MCU's have up to 50 GPIO - #endif - struct { - uint8_t i2cAllocCount : 4; // allow multiple allocation of I2C bus pins but keep track of allocations - uint8_t spiAllocCount : 4; // allow multiple allocation of SPI bus pins but keep track of allocations - }; + struct { + #ifdef ESP8266 + #define WLED_NUM_PINS (GPIO_PIN_COUNT+1) // somehow they forgot GPIO 16 (0-16==17) + uint32_t pinAlloc : 24; // 24bit, 1 bit per pin, we use first 17bits + #else + #define WLED_NUM_PINS (GPIO_PIN_COUNT) + uint64_t pinAlloc : 56; // 56 bits, 1 bit per pin, we use 50 bits on ESP32-S3 + uint16_t ledcAlloc : 16; // up to 16 LEDC channels (WLED_MAX_ANALOG_CHANNELS) + #endif + uint8_t i2cAllocCount : 4; // allow multiple allocation of I2C bus pins but keep track of allocations + uint8_t spiAllocCount : 4; // allow multiple allocation of SPI bus pins but keep track of allocations + } __attribute__ ((packed)); + PinOwner ownerTag[WLED_NUM_PINS] = { PinOwner::None }; public: - PinManagerClass() : i2cAllocCount(0), spiAllocCount(0) {} + PinManagerClass() : pinAlloc(0), i2cAllocCount(0), spiAllocCount(0) {} // De-allocates a single pin bool deallocatePin(byte gpio, PinOwner tag); // De-allocates multiple pins but only if all can be deallocated (PinOwner has to be specified) @@ -101,13 +102,9 @@ class PinManagerClass { // ethernet, etc.. bool allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag ); - #if !defined(ESP8266) // ESP8266 compiler doesn't understand deprecated attribute [[deprecated("Replaced by three-parameter allocatePin(gpio, output, ownerTag), for improved debugging")]] - #endif inline bool allocatePin(byte gpio, bool output = true) { return allocatePin(gpio, output, PinOwner::None); } - #if !defined(ESP8266) // ESP8266 compiler doesn't understand deprecated attribute [[deprecated("Replaced by two-parameter deallocatePin(gpio, ownerTag), for improved debugging")]] - #endif inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); } // will return true for reserved pins