Merge pull request #2415 from Aircoookie/i2c-sharing

Pin manager support for sharing multipin buses.
This commit is contained in:
Blaž Kristan 2021-12-16 19:57:08 +01:00 committed by GitHub
commit c27117e99e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 79 additions and 14 deletions

View File

@ -1,6 +1,7 @@
#include "pin_manager.h" #include "pin_manager.h"
#include "wled.h" #include "wled.h"
#ifdef WLED_DEBUG
static void DebugPrintOwnerTag(PinOwner tag) static void DebugPrintOwnerTag(PinOwner tag)
{ {
uint32_t q = static_cast<uint8_t>(tag); uint32_t q = static_cast<uint8_t>(tag);
@ -10,6 +11,7 @@ static void DebugPrintOwnerTag(PinOwner tag)
DEBUG_PRINT(F("(no owner)")); DEBUG_PRINT(F("(no owner)"));
} }
} }
#endif
/// Actual allocation/deallocation routines /// Actual allocation/deallocation routines
bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag) 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 a non-zero ownerTag, only allow de-allocation if the owner's tag is provided
if ((ownerTag[gpio] != PinOwner::None) && (ownerTag[gpio] != tag)) { if ((ownerTag[gpio] != PinOwner::None) && (ownerTag[gpio] != tag)) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN DEALLOC: IO ")); DEBUG_PRINT(F("PIN DEALLOC: IO "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" allocated by ")); DEBUG_PRINT(F(" allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]); DebugPrintOwnerTag(ownerTag[gpio]);
DEBUG_PRINT(F(", but attempted de-allocation by ")); DEBUG_PRINT(F(", but attempted de-allocation by "));
DebugPrintOwnerTag(tag); DebugPrintOwnerTag(tag);
#endif
return false; return false;
} }
@ -34,6 +38,49 @@ bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag)
ownerTag[gpio] = PinOwner::None; ownerTag[gpio] = PinOwner::None;
return true; 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 PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag)
{ {
bool shouldFail = false; bool shouldFail = false;
@ -46,17 +93,24 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
continue; continue;
} }
if (!isPinOk(gpio, mptArray[i].isOutput)) { if (!isPinOk(gpio, mptArray[i].isOutput)) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Invalid pin attempted to be allocated: ")); DEBUG_PRINT(F("PIN ALLOC: Invalid pin attempted to be allocated: "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINTLN(F("")); DEBUG_PRINTLN(F(""));
#endif
shouldFail = true; 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(F("PIN ALLOC: FAIL: IO "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" already allocated by ")); DEBUG_PRINT(F(" already allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]); DebugPrintOwnerTag(ownerTag[gpio]);
DEBUG_PRINTLN(F("")); DEBUG_PRINTLN(F(""));
#endif
shouldFail = true; shouldFail = true;
} }
} }
@ -64,6 +118,8 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
return false; return false;
} }
if (tag==PinOwner::HW_I2C) i2cAllocCount++;
// all pins are available .. track each one // all pins are available .. track each one
for (int i = 0; i < arrayElementCount; i++) { for (int i = 0; i < arrayElementCount; i++) {
byte gpio = mptArray[i].pin; byte gpio = mptArray[i].pin;
@ -79,15 +135,19 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
} }
return true; return true;
} }
bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag) 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)) { if (isPinAllocated(gpio)) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Pin ")); DEBUG_PRINT(F("PIN ALLOC: Pin "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" already allocated by ")); DEBUG_PRINT(F(" already allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]); DebugPrintOwnerTag(ownerTag[gpio]);
DEBUG_PRINTLN(F("")); DEBUG_PRINTLN(F(""));
#endif
return false; return false;
} }
@ -106,7 +166,7 @@ bool PinManagerClass::isPinAllocated(byte gpio, PinOwner tag)
if (!isPinOk(gpio, false)) return true; if (!isPinOk(gpio, false)) return true;
if ((tag != PinOwner::None) && (ownerTag[gpio] != tag)) return false; if ((tag != PinOwner::None) && (ownerTag[gpio] != tag)) return false;
byte by = gpio >> 3; byte by = gpio >> 3;
byte bi = gpio - 8*by; byte bi = gpio - (by<<3);
return bitRead(pinAlloc[by], bi); return bitRead(pinAlloc[by], bi);
} }

View File

@ -7,8 +7,8 @@
#include "const.h" // for USERMOD_* values #include "const.h" // for USERMOD_* values
typedef struct PinManagerPinType { typedef struct PinManagerPinType {
int8_t pin; int8_t pin;
uint8_t isOutput; bool isOutput;
} managed_pin_type; } managed_pin_type;
/* /*
@ -21,9 +21,8 @@ typedef struct PinManagerPinType {
* 40 bytes on ESP32 * 40 bytes on ESP32
*/ */
enum struct PinOwner : uint8_t { 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 // High bit is set for all built-in pin owners
// StatusLED -- THIS SHOULD NEVER BE ALLOCATED -- see handleStatusLED()
Ethernet = 0x81, Ethernet = 0x81,
BusDigital = 0x82, BusDigital = 0x82,
BusDigital2 = 0x83, BusDigital2 = 0x83,
@ -34,26 +33,27 @@ enum struct PinOwner : uint8_t {
SPI_RAM = 0x88, // 'SpiR' == SPI RAM SPI_RAM = 0x88, // 'SpiR' == SPI RAM
DebugOut = 0x89, // 'Dbg' == debug output always IO1 DebugOut = 0x89, // 'Dbg' == debug output always IO1
DMX = 0x8A, // 'DMX' == hard-coded to IO2 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 // Use UserMod IDs from const.h here
UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01 UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01
UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h" UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h"
UM_Temperature = USERMOD_ID_TEMPERATURE, // 0x03 // Usermod "usermod_temperature.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 // #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" 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 // #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 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" 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_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_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_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_MultiRelay = USERMOD_ID_MULTI_RELAY, // 0x0D // Usermod "usermod_multi_relay.h"
UM_AnimatedStaircase = USERMOD_ID_ANIMATED_STAIRCASE, // 0x0E // Usermod "Animated_Staircase.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_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 // #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_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_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA // 0x17 // Usermod "quinled-an-penta.h"
}; };
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); static_assert(0u == static_cast<uint8_t>(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 uint8_t ledcAlloc[2] = {0x00, 0x00}; //16 LEDC channels
PinOwner ownerTag[40] = { PinOwner::None }; PinOwner ownerTag[40] = { PinOwner::None };
#endif #endif
uint8_t i2cAllocCount = 0; // allow multiple allocation of I2C bus pins but keep track of allocations
public: public:
// De-allocates a single pin // De-allocates a single pin
bool deallocatePin(byte gpio, PinOwner tag); 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. // Allocates a single pin, with an owner tag.
// De-allocation requires the same owner tag (or override) // De-allocation requires the same owner tag (or override)
bool allocatePin(byte gpio, bool output, PinOwner tag); bool allocatePin(byte gpio, bool output, PinOwner tag);
@ -85,11 +88,13 @@ class PinManagerClass {
#endif #endif
inline bool allocatePin(byte gpio, bool output = true) { return allocatePin(gpio, output, PinOwner::None); } inline bool allocatePin(byte gpio, bool output = true) { return allocatePin(gpio, output, PinOwner::None); }
#if !defined(ESP8266) // ESP8266 compiler doesn't understand deprecated attribute #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 #endif
inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); } inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); }
// will return true for reserved pins
bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None); bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None);
// will return false for reserved pins
bool isPinOk(byte gpio, bool output = true); bool isPinOk(byte gpio, bool output = true);
PinOwner getPinOwner(byte gpio); PinOwner getPinOwner(byte gpio);