From 41c9bb63a0b5d64adee4b447a06dc1958584a70b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Tue, 14 Dec 2021 09:38:38 +0100 Subject: [PATCH] Pin manager support for sharing multipin buses. --- wled00/pin_manager.cpp | 66 ++++++++++++++++++++++++++++++++++++++++-- wled00/pin_manager.h | 27 ++++++++++------- 2 files changed, 79 insertions(+), 14 deletions(-) diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index ece48c24..2b908557 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -1,6 +1,7 @@ #include "pin_manager.h" #include "wled.h" +#ifdef WLED_DEBUG static void DebugPrintOwnerTag(PinOwner tag) { uint32_t q = static_cast(tag); @@ -10,6 +11,7 @@ static void DebugPrintOwnerTag(PinOwner tag) DEBUG_PRINT(F("(no owner)")); } } +#endif /// Actual allocation/deallocation routines bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag) @@ -19,12 +21,14 @@ bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag) // if a non-zero ownerTag, only allow de-allocation if the owner's tag is provided if ((ownerTag[gpio] != PinOwner::None) && (ownerTag[gpio] != tag)) { + #ifdef WLED_DEBUG DEBUG_PRINT(F("PIN DEALLOC: IO ")); DEBUG_PRINT(gpio); DEBUG_PRINT(F(" allocated by ")); DebugPrintOwnerTag(ownerTag[gpio]); DEBUG_PRINT(F(", but attempted de-allocation by ")); DebugPrintOwnerTag(tag); + #endif return false; } @@ -34,6 +38,49 @@ bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag) ownerTag[gpio] = PinOwner::None; return true; } + +// support function for deallocating multiple pins +bool PinManagerClass::deallocateMultiplePins(const uint8_t *pinArray, byte arrayElementCount, PinOwner tag) +{ + bool shouldFail = false; + DEBUG_PRINTLN(F("MULTIPIN DEALLOC")); + // first verify the pins are OK and allocated by selected owner + for (int i = 0; i < arrayElementCount; i++) { + byte gpio = pinArray[i]; + if (gpio == 0xFF) { + // explicit support for io -1 as a no-op (no allocation of pin), + // as this can greatly simplify configuration arrays + continue; + } + if (isPinAllocated(gpio, tag)) { + // if the current pin is allocated by selected owner it is possible to release it + continue; + } + #ifdef WLED_DEBUG + DEBUG_PRINT(F("PIN DEALLOC: IO ")); + DEBUG_PRINT(gpio); + DEBUG_PRINT(F(" allocated by ")); + DebugPrintOwnerTag(ownerTag[gpio]); + DEBUG_PRINT(F(", but attempted de-allocation by ")); + DebugPrintOwnerTag(tag); + #endif + shouldFail = true; + } + if (shouldFail) { + return false; // no pins deallocated + } + if (tag==PinOwner::HW_I2C) { + if (i2cAllocCount && --i2cAllocCount>0) { + // no deallocation done until last owner releases pins + return true; + } + } + for (int i = 0; i < arrayElementCount; i++) { + deallocatePin(pinArray[i], tag); + } + return true; +} + bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag) { bool shouldFail = false; @@ -46,17 +93,24 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by continue; } if (!isPinOk(gpio, mptArray[i].isOutput)) { + #ifdef WLED_DEBUG DEBUG_PRINT(F("PIN ALLOC: Invalid pin attempted to be allocated: ")); DEBUG_PRINT(gpio); DEBUG_PRINTLN(F("")); + #endif shouldFail = true; } - if (isPinAllocated(gpio)) { + if (tag==PinOwner::HW_I2C && isPinAllocated(gpio, tag)) { + // allow multiple "allocations" of HW I2C bus pins + continue; + } else if (isPinAllocated(gpio)) { + #ifdef WLED_DEBUG DEBUG_PRINT(F("PIN ALLOC: FAIL: IO ")); DEBUG_PRINT(gpio); DEBUG_PRINT(F(" already allocated by ")); DebugPrintOwnerTag(ownerTag[gpio]); DEBUG_PRINTLN(F("")); + #endif shouldFail = true; } } @@ -64,6 +118,8 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by return false; } + if (tag==PinOwner::HW_I2C) i2cAllocCount++; + // all pins are available .. track each one for (int i = 0; i < arrayElementCount; i++) { byte gpio = mptArray[i].pin; @@ -79,15 +135,19 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by } return true; } + bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag) { - if (!isPinOk(gpio, output)) return false; + // HW I2C pins have to be allocated using allocateMultiplePins variant since there is always SCL/SDA pair + if (!isPinOk(gpio, output) || tag==PinOwner::HW_I2C) return false; if (isPinAllocated(gpio)) { + #ifdef WLED_DEBUG DEBUG_PRINT(F("PIN ALLOC: Pin ")); DEBUG_PRINT(gpio); DEBUG_PRINT(F(" already allocated by ")); DebugPrintOwnerTag(ownerTag[gpio]); DEBUG_PRINTLN(F("")); + #endif return false; } @@ -106,7 +166,7 @@ bool PinManagerClass::isPinAllocated(byte gpio, PinOwner tag) if (!isPinOk(gpio, false)) return true; if ((tag != PinOwner::None) && (ownerTag[gpio] != tag)) return false; byte by = gpio >> 3; - byte bi = gpio - 8*by; + byte bi = gpio - (by<<3); return bitRead(pinAlloc[by], bi); } diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index 65d31966..5f009425 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -7,8 +7,8 @@ #include "const.h" // for USERMOD_* values typedef struct PinManagerPinType { - int8_t pin; - uint8_t isOutput; + int8_t pin; + bool isOutput; } managed_pin_type; /* @@ -21,9 +21,8 @@ typedef struct PinManagerPinType { * 40 bytes on ESP32 */ enum struct PinOwner : uint8_t { - None = 0, // default == legacy == unspecified owner + None = 0, // default == legacy == unspecified owner // High bit is set for all built-in pin owners - // StatusLED -- THIS SHOULD NEVER BE ALLOCATED -- see handleStatusLED() Ethernet = 0x81, BusDigital = 0x82, BusDigital2 = 0x83, @@ -34,26 +33,27 @@ enum struct PinOwner : uint8_t { SPI_RAM = 0x88, // 'SpiR' == SPI RAM DebugOut = 0x89, // 'Dbg' == debug output always IO1 DMX = 0x8A, // 'DMX' == hard-coded to IO2 + HW_I2C = 0x8B, // 'I2C' == hardware I2C pins (4&5 on ESP8266, 21&22 on ESP32) // Use UserMod IDs from const.h here UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01 UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h" UM_Temperature = USERMOD_ID_TEMPERATURE, // 0x03 // Usermod "usermod_temperature.h" // #define USERMOD_ID_FIXNETSERVICES // 0x04 // Usermod "usermod_Fix_unreachable_netservices.h" -- Does not allocate pins UM_PIR = USERMOD_ID_PIRSWITCH, // 0x05 // Usermod "usermod_PIR_sensor_switch.h" - // #define USERMOD_ID_IMU // 0x06 // Usermod "usermod_mpu6050_imu.h" -- Uses "standard" I2C pins ... TODO -- enable shared I2C bus use - UM_FourLineDisplay = USERMOD_ID_FOUR_LINE_DISP, // 0x07 // Usermod "usermod_v2_four_line_display.h + // #define USERMOD_ID_IMU // 0x06 // Usermod "usermod_mpu6050_imu.h" -- Uses "standard" HW_I2C pins + UM_FourLineDisplay = USERMOD_ID_FOUR_LINE_DISP, // 0x07 // Usermod "usermod_v2_four_line_display.h -- May use "standard" HW_I2C pins 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" I2C pins ... TODO -- enable shared I2C bus use + // #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" - // #define USERMOD_ID_RTC // 0x0F // Usermod "usermod_rtc.h" -- Uses "standard" I2C pins ... TODO -- enable shared I2C bus use + // #define USERMOD_ID_RTC // 0x0F // Usermod "usermod_rtc.h" -- Uses "standard" HW_I2C pins // #define USERMOD_ID_ELEKSTUBE_IPS // 0x10 // Usermod "usermod_elekstube_ips.h" -- Uses quite a few pins ... see Hardware.h and User_Setup.h // #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager - UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h" - UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h" + UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h" + UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA // 0x17 // Usermod "quinled-an-penta.h" }; static_assert(0u == static_cast(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); @@ -67,10 +67,13 @@ class PinManagerClass { uint8_t ledcAlloc[2] = {0x00, 0x00}; //16 LEDC channels PinOwner ownerTag[40] = { PinOwner::None }; #endif + uint8_t i2cAllocCount = 0; // allow multiple allocation of I2C bus pins but keep track of allocations public: // 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) + bool deallocateMultiplePins(const uint8_t *pinArray, byte arrayElementCount, PinOwner tag); // Allocates a single pin, with an owner tag. // De-allocation requires the same owner tag (or override) bool allocatePin(byte gpio, bool output, PinOwner tag); @@ -85,11 +88,13 @@ class PinManagerClass { #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 three-parameter deallocatePin(gpio, output, ownerTag), for improved debugging")]] + [[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 bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None); + // will return false for reserved pins bool isPinOk(byte gpio, bool output = true); PinOwner getPinOwner(byte gpio);