2021-01-15 23:50:43 +00:00
# include "pin_manager.h"
2020-11-15 11:41:51 +00:00
# include "wled.h"
2024-09-17 14:21:52 +00:00
# ifdef ARDUINO_ARCH_ESP32
# ifdef bitRead
// Arduino variants assume 32 bit values
# undef bitRead
# undef bitSet
# undef bitClear
# define bitRead(var,bit) (((unsigned long long)(var)>>(bit))&0x1ULL)
# define bitSet(var,bit) ((var)|=(1ULL<<(bit)))
# define bitClear(var,bit) ((var)&=(~(1ULL<<(bit))))
# endif
# endif
2021-08-23 12:14:48 +00:00
/// Actual allocation/deallocation routines
2024-09-19 19:44:11 +00:00
bool PinManager : : deallocatePin ( byte gpio , PinOwner tag )
2021-08-23 12:14:48 +00:00
{
if ( gpio = = 0xFF ) return true ; // explicitly allow clients to free -1 as a no-op
if ( ! isPinOk ( gpio , false ) ) return false ; // but return false for any other invalid pin
// if a non-zero ownerTag, only allow de-allocation if the owner's tag is provided
if ( ( ownerTag [ gpio ] ! = PinOwner : : None ) & & ( ownerTag [ gpio ] ! = tag ) ) {
2024-09-19 19:44:11 +00:00
DEBUG_PRINTF_P ( PSTR ( " PIN DEALLOC: FAIL GPIO %d allocated by 0x%02X, but attempted de-allocation by 0x%02X. \n " ) , gpio , static_cast < int > ( ownerTag [ gpio ] ) , static_cast < int > ( tag ) ) ;
2021-08-23 12:14:48 +00:00
return false ;
}
2023-01-06 08:24:29 +00:00
2024-08-26 22:21:24 +00:00
bitWrite ( pinAlloc , gpio , false ) ;
2021-08-23 12:14:48 +00:00
ownerTag [ gpio ] = PinOwner : : None ;
return true ;
2020-11-15 11:41:51 +00:00
}
2021-12-14 08:38:38 +00:00
// support function for deallocating multiple pins
2024-09-19 19:44:11 +00:00
bool PinManager : : deallocateMultiplePins ( const uint8_t * pinArray , byte arrayElementCount , PinOwner tag )
2021-12-14 08:38:38 +00:00
{
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 ;
}
2024-09-19 19:44:11 +00:00
DEBUG_PRINTF_P ( PSTR ( " PIN DEALLOC: FAIL GPIO %d allocated by 0x%02X, but attempted de-allocation by 0x%02X. \n " ) , gpio , static_cast < int > ( ownerTag [ gpio ] ) , static_cast < int > ( tag ) ) ;
2021-12-14 08:38:38 +00:00
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 ;
}
}
2022-08-14 11:05:59 +00:00
if ( tag = = PinOwner : : HW_SPI ) {
if ( spiAllocCount & & - - spiAllocCount > 0 ) {
// no deallocation done until last owner releases pins
return true ;
}
}
2021-12-14 08:38:38 +00:00
for ( int i = 0 ; i < arrayElementCount ; i + + ) {
deallocatePin ( pinArray [ i ] , tag ) ;
}
return true ;
}
2024-09-19 19:44:11 +00:00
bool PinManager : : deallocateMultiplePins ( const managed_pin_type * mptArray , byte arrayElementCount , PinOwner tag )
2022-08-14 11:05:59 +00:00
{
uint8_t pins [ arrayElementCount ] ;
for ( int i = 0 ; i < arrayElementCount ; i + + ) pins [ i ] = mptArray [ i ] . pin ;
return deallocateMultiplePins ( pins , arrayElementCount , tag ) ;
}
2024-09-19 19:44:11 +00:00
bool PinManager : : allocateMultiplePins ( const managed_pin_type * mptArray , byte arrayElementCount , PinOwner tag )
2021-08-23 12:14:48 +00:00
{
bool shouldFail = false ;
// first verify the pins are OK and not already allocated
for ( int i = 0 ; i < arrayElementCount ; i + + ) {
2024-09-06 15:56:36 +00:00
byte gpio = mptArray [ i ] . pin ;
2021-08-23 12:14:48 +00:00
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 ( ! isPinOk ( gpio , mptArray [ i ] . isOutput ) ) {
2024-09-19 19:44:11 +00:00
DEBUG_PRINTF_P ( PSTR ( " PIN ALLOC: FAIL Invalid pin attempted to be allocated: GPIO %d as %s \n . " ) , gpio , mptArray [ i ] . isOutput ? PSTR ( " output " ) : PSTR ( " input " ) ) ;
2021-08-23 12:14:48 +00:00
shouldFail = true ;
}
2022-08-14 11:05:59 +00:00
if ( ( tag = = PinOwner : : HW_I2C | | tag = = PinOwner : : HW_SPI ) & & isPinAllocated ( gpio , tag ) ) {
// allow multiple "allocations" of HW I2C & SPI bus pins
2021-12-14 08:38:38 +00:00
continue ;
} else if ( isPinAllocated ( gpio ) ) {
2024-09-19 19:44:11 +00:00
DEBUG_PRINTF_P ( PSTR ( " PIN ALLOC: FAIL GPIO %d already allocated by 0x%02X. \n " ) , gpio , static_cast < int > ( ownerTag [ gpio ] ) ) ;
2021-08-23 12:14:48 +00:00
shouldFail = true ;
}
}
if ( shouldFail ) {
return false ;
}
2020-11-15 11:41:51 +00:00
2021-12-14 08:38:38 +00:00
if ( tag = = PinOwner : : HW_I2C ) i2cAllocCount + + ;
2022-08-14 11:05:59 +00:00
if ( tag = = PinOwner : : HW_SPI ) spiAllocCount + + ;
2021-12-14 08:38:38 +00:00
2021-08-23 12:14:48 +00:00
// all pins are available .. track each one
for ( int i = 0 ; i < arrayElementCount ; i + + ) {
2024-09-06 15:56:36 +00:00
byte gpio = mptArray [ i ] . pin ;
2021-08-23 12:14:48 +00:00
if ( gpio = = 0xFF ) {
// allow callers to include -1 value as non-requested pin
// as this can greatly simplify configuration arrays
continue ;
}
2023-01-06 08:24:29 +00:00
if ( gpio > = WLED_NUM_PINS )
2022-12-05 16:04:54 +00:00
continue ; // other unexpected GPIO => avoid array bounds violation
2024-08-26 22:21:24 +00:00
bitWrite ( pinAlloc , gpio , true ) ;
2021-08-23 12:14:48 +00:00
ownerTag [ gpio ] = tag ;
2024-09-19 19:44:11 +00:00
DEBUG_PRINTF_P ( PSTR ( " PIN ALLOC: Pin %d allocated by 0x%02X. \n " ) , gpio , static_cast < int > ( tag ) ) ;
2021-08-23 12:14:48 +00:00
}
2024-09-19 19:44:11 +00:00
DEBUG_PRINTF_P ( PSTR ( " PIN ALLOC: 0x%014llX. \n " ) , ( unsigned long long ) pinAlloc ) ;
2021-08-23 12:14:48 +00:00
return true ;
}
2021-12-14 08:38:38 +00:00
2024-09-19 19:44:11 +00:00
bool PinManager : : allocatePin ( byte gpio , bool output , PinOwner tag )
2020-11-15 11:41:51 +00:00
{
2022-08-14 11:05:59 +00:00
// HW I2C & SPI pins have to be allocated using allocateMultiplePins variant since there is always SCL/SDA pair
2022-12-05 16:04:54 +00:00
if ( ! isPinOk ( gpio , output ) | | ( gpio > = WLED_NUM_PINS ) | | tag = = PinOwner : : HW_I2C | | tag = = PinOwner : : HW_SPI ) {
2022-09-17 18:42:34 +00:00
# ifdef WLED_DEBUG
if ( gpio < 255 ) { // 255 (-1) is the "not defined GPIO"
if ( ! isPinOk ( gpio , output ) ) {
2024-09-19 19:44:11 +00:00
DEBUG_PRINTF_P ( PSTR ( " PIN ALLOC: FAIL for owner 0x%02X: GPIO %d " ) , static_cast < int > ( tag ) , gpio ) ;
2022-09-17 18:42:34 +00:00
if ( output ) DEBUG_PRINTLN ( F ( " cannot be used for i/o on this MCU. " ) ) ;
else DEBUG_PRINTLN ( F ( " cannot be used as input on this MCU. " ) ) ;
} else {
2024-09-19 19:44:11 +00:00
DEBUG_PRINTF_P ( PSTR ( " PIN ALLOC: FAIL GPIO %d - HW I2C & SPI pins have to be allocated using allocateMultiplePins. \n " ) , gpio ) ;
2022-09-17 18:42:34 +00:00
}
}
# endif
return false ;
}
2020-11-15 11:41:51 +00:00
if ( isPinAllocated ( gpio ) ) {
2024-09-19 19:44:11 +00:00
DEBUG_PRINTF_P ( PSTR ( " PIN ALLOC: FAIL Pin %d already allocated by 0x%02X. \n " ) , gpio , static_cast < int > ( ownerTag [ gpio ] ) ) ;
2020-11-15 11:41:51 +00:00
return false ;
}
2024-08-26 22:21:24 +00:00
bitWrite ( pinAlloc , gpio , true ) ;
2021-08-23 12:14:48 +00:00
ownerTag [ gpio ] = tag ;
2024-09-19 19:44:11 +00:00
DEBUG_PRINTF_P ( PSTR ( " PIN ALLOC: Pin %d successfully allocated by 0x%02X. \n " ) , gpio , static_cast < int > ( ownerTag [ gpio ] ) ) ;
DEBUG_PRINTF_P ( PSTR ( " PIN ALLOC: 0x%014llX. \n " ) , ( unsigned long long ) pinAlloc ) ;
2022-02-07 23:03:20 +00:00
2020-11-15 11:41:51 +00:00
return true ;
}
2021-08-23 12:14:48 +00:00
// if tag is set to PinOwner::None, checks for ANY owner of the pin.
// if tag is set to any other value, checks if that tag is the current owner of the pin.
2024-09-19 19:44:11 +00:00
bool PinManager : : isPinAllocated ( byte gpio , PinOwner tag )
2020-11-15 11:41:51 +00:00
{
if ( ! isPinOk ( gpio , false ) ) return true ;
2021-08-23 12:14:48 +00:00
if ( ( tag ! = PinOwner : : None ) & & ( ownerTag [ gpio ] ! = tag ) ) return false ;
2024-08-26 22:21:24 +00:00
return bitRead ( pinAlloc , gpio ) ;
2020-11-15 11:41:51 +00:00
}
2022-09-24 10:25:06 +00:00
/* see https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/gpio.html
* The ESP32 - S3 chip features 45 physical GPIO pins ( GPIO0 ~ GPIO21 and GPIO26 ~ GPIO48 ) . Each pin can be used as a general - purpose I / O
* Strapping pins : GPIO0 , GPIO3 , GPIO45 and GPIO46 are strapping pins . For more infomation , please refer to ESP32 - S3 datasheet .
* Serial TX = GPIO43 , RX = GPIO44 ; LED BUILTIN is usually GPIO39
* USB - JTAG : GPIO 19 and 20 are used by USB - JTAG by default . In order to use them as GPIOs , USB - JTAG will be disabled by the drivers .
2023-01-06 08:24:29 +00:00
* SPI0 / 1 : GPIO26 - 32 are usually used for SPI flash and PSRAM and not recommended for other uses .
2022-09-24 10:25:06 +00:00
* When using Octal Flash or Octal PSRAM or both , GPIO33 ~ 37 are connected to SPIIO4 ~ SPIIO7 and SPIDQS . Therefore , on boards embedded with ESP32 - S3R8 / ESP32 - S3R8V chip , GPIO33 ~ 37 are also not recommended for other uses .
2023-01-06 08:24:29 +00:00
*
2022-09-24 10:25:06 +00:00
* see https : //docs.espressif.com/projects/esp-idf/en/v4.4.2/esp32s3/api-reference/peripherals/adc.html
* https : //docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/adc_oneshot.html
* ADC1 : GPIO1 - GPIO10 ( channel 0. .9 )
* ADC2 : GPIO11 - GPIO20 ( channel 0. .9 )
* adc_power_acquire ( ) : Please do not use the interrupt of GPIO36 and GPIO39 when using ADC or Wi - Fi and Bluetooth with sleep mode enabled . As a workaround , call adc_power_acquire ( ) in the APP .
* Since the ADC2 module is also used by the Wi - Fi , reading operation of adc2_get_raw ( ) may fail between esp_wifi_start ( ) and esp_wifi_stop ( ) . Use the return code to see whether the reading is successful .
*/
// Check if supplied GPIO is ok to use
2024-09-19 19:44:11 +00:00
bool PinManager : : isPinOk ( byte gpio , bool output )
2022-09-10 22:09:59 +00:00
{
2024-08-26 22:21:24 +00:00
if ( gpio > = WLED_NUM_PINS ) return false ; // catch error case, to avoid array out-of-bounds access
2023-12-21 20:30:17 +00:00
# ifdef ARDUINO_ARCH_ESP32
2022-09-24 10:25:06 +00:00
if ( digitalPinIsValid ( gpio ) ) {
2022-09-13 17:50:13 +00:00
# if defined(CONFIG_IDF_TARGET_ESP32C3)
2022-09-24 10:25:06 +00:00
// strapping pins: 2, 8, & 9
if ( gpio > 11 & & gpio < 18 ) return false ; // 11-17 SPI FLASH
2024-06-29 18:22:47 +00:00
# if ARDUINO_USB_CDC_ON_BOOT == 1 || ARDUINO_USB_DFU_ON_BOOT == 1
2022-09-24 10:25:06 +00:00
if ( gpio > 17 & & gpio < 20 ) return false ; // 18-19 USB-JTAG
2024-06-29 18:22:47 +00:00
# endif
2022-09-24 10:25:06 +00:00
# elif defined(CONFIG_IDF_TARGET_ESP32S3)
// 00 to 18 are for general use. Be careful about straping pins GPIO0 and GPIO3 - these may be pulled-up or pulled-down on your board.
2024-06-29 18:22:47 +00:00
# if ARDUINO_USB_CDC_ON_BOOT == 1 || ARDUINO_USB_DFU_ON_BOOT == 1
2022-09-24 10:25:06 +00:00
if ( gpio > 18 & & gpio < 21 ) return false ; // 19 + 20 = USB-JTAG. Not recommended for other uses.
2024-06-29 18:22:47 +00:00
# endif
2022-09-24 10:25:06 +00:00
if ( gpio > 21 & & gpio < 33 ) return false ; // 22 to 32: not connected + SPI FLASH
2024-11-01 20:31:57 +00:00
# if CONFIG_ESPTOOLPY_FLASHMODE_OPI // 33-37: never available if using _octal_ Flash (opi_opi)
if ( gpio > 32 & & gpio < 38 ) return false ;
# endif
# if CONFIG_SPIRAM_MODE_OCT // 33-37: not available if using _octal_ PSRAM (qio_opi), but free to use on _quad_ PSRAM (qio_qspi)
if ( gpio > 32 & & gpio < 38 ) return ! psramFound ( ) ;
# endif
2022-09-24 10:25:06 +00:00
// 38 to 48 are for general use. Be careful about straping pins GPIO45 and GPIO46 - these may be pull-up or pulled-down on your board.
# elif defined(CONFIG_IDF_TARGET_ESP32S2)
// strapping pins: 0, 45 & 46
if ( gpio > 21 & & gpio < 33 ) return false ; // 22 to 32: not connected + SPI FLASH
// JTAG: GPIO39-42 are usually used for inline debugging
// GPIO46 is input only and pulled down
# else
if ( gpio > 5 & & gpio < 12 ) return false ; //SPI flash pins
2024-04-04 21:09:59 +00:00
if ( strncmp_P ( PSTR ( " ESP32-PICO " ) , ESP . getChipModel ( ) , 10 ) = = 0 & & ( gpio = = 16 | | gpio = = 17 ) ) return false ; // PICO-D4: gpio16+17 are in use for onboard SPI FLASH
2024-03-24 16:37:11 +00:00
if ( gpio = = 16 | | gpio = = 17 ) return ! psramFound ( ) ; //PSRAM pins on ESP32 (these are IO)
2022-09-13 17:50:13 +00:00
# endif
2022-09-24 10:25:06 +00:00
if ( output ) return digitalPinCanOutput ( gpio ) ;
else return true ;
}
# else
if ( gpio < 6 ) return true ;
2020-11-15 11:41:51 +00:00
if ( gpio < 12 ) return false ; //SPI flash pins
if ( gpio < 17 ) return true ;
2022-09-24 10:25:06 +00:00
# endif
2020-11-15 11:41:51 +00:00
return false ;
2020-12-07 00:39:42 +00:00
}
2024-09-19 19:44:11 +00:00
bool PinManager : : isReadOnlyPin ( byte gpio )
2024-09-01 19:31:19 +00:00
{
2024-09-14 09:39:56 +00:00
# ifdef ARDUINO_ARCH_ESP32
2024-09-14 16:58:53 +00:00
if ( gpio < WLED_NUM_PINS ) return ( digitalPinIsValid ( gpio ) & & ! digitalPinCanOutput ( gpio ) ) ;
2024-09-14 09:39:56 +00:00
# endif
2024-09-09 23:03:19 +00:00
return false ;
}
2024-09-19 19:44:11 +00:00
PinOwner PinManager : : getPinOwner ( byte gpio )
2024-03-24 16:37:11 +00:00
{
2021-11-17 10:13:07 +00:00
if ( ! isPinOk ( gpio , false ) ) return PinOwner : : None ;
return ownerTag [ gpio ] ;
}
2020-12-07 00:39:42 +00:00
# ifdef ARDUINO_ARCH_ESP32
2024-09-19 19:44:11 +00:00
byte PinManager : : allocateLedc ( byte channels )
2020-12-07 00:39:42 +00:00
{
2024-08-26 22:21:24 +00:00
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
2020-12-07 00:39:42 +00:00
ca = 0 ;
} else {
2024-08-26 22:21:24 +00:00
// 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 + + ;
2020-12-07 00:39:42 +00:00
}
if ( ca > = channels ) { //enough free channels
2024-08-26 22:21:24 +00:00
unsigned in = ( i + 1 ) - ca ;
2024-07-11 19:22:58 +00:00
for ( unsigned j = 0 ; j < ca ; j + + ) {
2024-08-26 22:21:24 +00:00
bitWrite ( ledcAlloc , in + j , true ) ;
2020-12-07 00:39:42 +00:00
}
return in ;
}
}
return 255 ; //not enough consecutive free LEDC channels
}
2024-09-19 19:44:11 +00:00
void PinManager : : deallocateLedc ( byte pos , byte channels )
2020-12-07 00:39:42 +00:00
{
2024-08-26 22:21:24 +00:00
for ( unsigned j = pos ; j < pos + channels & & j < WLED_MAX_ANALOG_CHANNELS ; j + + ) {
bitWrite ( ledcAlloc , j , false ) ;
2020-12-07 00:39:42 +00:00
}
}
2021-01-15 23:50:43 +00:00
# endif
2024-09-19 19:44:11 +00:00
# ifdef ESP8266
uint32_t PinManager : : pinAlloc = 0UL ;
# else
uint64_t PinManager : : pinAlloc = 0ULL ;
uint16_t PinManager : : ledcAlloc = 0 ;
# endif
uint8_t PinManager : : i2cAllocCount = 0 ;
uint8_t PinManager : : spiAllocCount = 0 ;
PinOwner PinManager : : ownerTag [ WLED_NUM_PINS ] = { PinOwner : : None } ;