mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	[esp32][esp8266] use low-level pin control for ISR gpio (#8743)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
This commit is contained in:
		| @@ -2,42 +2,66 @@ | |||||||
|  |  | ||||||
| #include "gpio.h" | #include "gpio.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  | #include "driver/gpio.h" | ||||||
|  | #include "driver/rtc_io.h" | ||||||
|  | #include "hal/gpio_hal.h" | ||||||
|  | #include "soc/soc_caps.h" | ||||||
|  | #include "soc/gpio_periph.h" | ||||||
| #include <cinttypes> | #include <cinttypes> | ||||||
|  |  | ||||||
|  | #if (SOC_RTCIO_PIN_COUNT > 0) | ||||||
|  | #include "hal/rtc_io_hal.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifndef SOC_GPIO_SUPPORT_RTC_INDEPENDENT | ||||||
|  | #define SOC_GPIO_SUPPORT_RTC_INDEPENDENT 0  // NOLINT | ||||||
|  | #endif | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace esp32 { | namespace esp32 { | ||||||
|  |  | ||||||
| static const char *const TAG = "esp32"; | static const char *const TAG = "esp32"; | ||||||
|  |  | ||||||
|  | static const gpio_hal_context_t GPIO_HAL = {.dev = GPIO_HAL_GET_HW(GPIO_PORT_0)}; | ||||||
|  |  | ||||||
| bool ESP32InternalGPIOPin::isr_service_installed = false;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) | bool ESP32InternalGPIOPin::isr_service_installed = false;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) | ||||||
|  |  | ||||||
| static gpio_mode_t IRAM_ATTR flags_to_mode(gpio::Flags flags) { | static gpio_mode_t flags_to_mode(gpio::Flags flags) { | ||||||
|   flags = (gpio::Flags)(flags & ~(gpio::FLAG_PULLUP | gpio::FLAG_PULLDOWN)); |   flags = (gpio::Flags)(flags & ~(gpio::FLAG_PULLUP | gpio::FLAG_PULLDOWN)); | ||||||
|   if (flags == gpio::FLAG_INPUT) { |   if (flags == gpio::FLAG_INPUT) | ||||||
|     return GPIO_MODE_INPUT; |     return GPIO_MODE_INPUT; | ||||||
|   } else if (flags == gpio::FLAG_OUTPUT) { |   if (flags == gpio::FLAG_OUTPUT) | ||||||
|     return GPIO_MODE_OUTPUT; |     return GPIO_MODE_OUTPUT; | ||||||
|   } else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { |   if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) | ||||||
|     return GPIO_MODE_OUTPUT_OD; |     return GPIO_MODE_OUTPUT_OD; | ||||||
|   } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { |   if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) | ||||||
|     return GPIO_MODE_INPUT_OUTPUT_OD; |     return GPIO_MODE_INPUT_OUTPUT_OD; | ||||||
|   } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT)) { |   if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT)) | ||||||
|     return GPIO_MODE_INPUT_OUTPUT; |     return GPIO_MODE_INPUT_OUTPUT; | ||||||
|   } else { |  | ||||||
|   // unsupported or gpio::FLAG_NONE |   // unsupported or gpio::FLAG_NONE | ||||||
|   return GPIO_MODE_DISABLE; |   return GPIO_MODE_DISABLE; | ||||||
| } | } | ||||||
| } |  | ||||||
|  |  | ||||||
| struct ISRPinArg { | struct ISRPinArg { | ||||||
|   gpio_num_t pin; |   gpio_num_t pin; | ||||||
|  |   gpio::Flags flags; | ||||||
|   bool inverted; |   bool inverted; | ||||||
|  | #if defined(USE_ESP32_VARIANT_ESP32) | ||||||
|  |   bool use_rtc; | ||||||
|  |   int rtc_pin; | ||||||
|  | #endif | ||||||
| }; | }; | ||||||
|  |  | ||||||
| ISRInternalGPIOPin ESP32InternalGPIOPin::to_isr() const { | ISRInternalGPIOPin ESP32InternalGPIOPin::to_isr() const { | ||||||
|   auto *arg = new ISRPinArg{};  // NOLINT(cppcoreguidelines-owning-memory) |   auto *arg = new ISRPinArg{};  // NOLINT(cppcoreguidelines-owning-memory) | ||||||
|   arg->pin = pin_; |   arg->pin = this->pin_; | ||||||
|  |   arg->flags = gpio::FLAG_NONE; | ||||||
|   arg->inverted = inverted_; |   arg->inverted = inverted_; | ||||||
|  | #if defined(USE_ESP32_VARIANT_ESP32) | ||||||
|  |   arg->use_rtc = rtc_gpio_is_valid_gpio(this->pin_); | ||||||
|  |   if (arg->use_rtc) | ||||||
|  |     arg->rtc_pin = rtc_io_number_get(this->pin_); | ||||||
|  | #endif | ||||||
|   return ISRInternalGPIOPin((void *) arg); |   return ISRInternalGPIOPin((void *) arg); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -90,6 +114,7 @@ void ESP32InternalGPIOPin::setup() { | |||||||
|   if (flags_ & gpio::FLAG_OUTPUT) { |   if (flags_ & gpio::FLAG_OUTPUT) { | ||||||
|     gpio_set_drive_capability(pin_, drive_strength_); |     gpio_set_drive_capability(pin_, drive_strength_); | ||||||
|   } |   } | ||||||
|  |   ESP_LOGD(TAG, "rtc: %d", SOC_GPIO_SUPPORT_RTC_INDEPENDENT); | ||||||
| } | } | ||||||
|  |  | ||||||
| void ESP32InternalGPIOPin::pin_mode(gpio::Flags flags) { | void ESP32InternalGPIOPin::pin_mode(gpio::Flags flags) { | ||||||
| @@ -115,28 +140,65 @@ void ESP32InternalGPIOPin::detach_interrupt() const { gpio_intr_disable(pin_); } | |||||||
| using namespace esp32; | using namespace esp32; | ||||||
|  |  | ||||||
| bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { | bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { | ||||||
|   auto *arg = reinterpret_cast<ISRPinArg *>(arg_); |   auto *arg = reinterpret_cast<ISRPinArg *>(this->arg_); | ||||||
|   return bool(gpio_get_level(arg->pin)) != arg->inverted; |   return bool(gpio_hal_get_level(&GPIO_HAL, arg->pin)) != arg->inverted; | ||||||
| } | } | ||||||
|  |  | ||||||
| void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) { | void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) { | ||||||
|   auto *arg = reinterpret_cast<ISRPinArg *>(arg_); |   auto *arg = reinterpret_cast<ISRPinArg *>(this->arg_); | ||||||
|   gpio_set_level(arg->pin, value != arg->inverted ? 1 : 0); |   gpio_hal_set_level(&GPIO_HAL, arg->pin, value != arg->inverted); | ||||||
| } | } | ||||||
|  |  | ||||||
| void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() { | void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() { | ||||||
|   // not supported |   // not supported | ||||||
| } | } | ||||||
|  |  | ||||||
| void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) { | void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) { | ||||||
|   auto *arg = reinterpret_cast<ISRPinArg *>(arg_); |   auto *arg = reinterpret_cast<ISRPinArg *>(arg_); | ||||||
|   gpio_set_direction(arg->pin, flags_to_mode(flags)); |   gpio::Flags diff = (gpio::Flags)(flags ^ arg->flags); | ||||||
|   gpio_pull_mode_t pull_mode = GPIO_FLOATING; |   if (diff & gpio::FLAG_OUTPUT) { | ||||||
|   if ((flags & gpio::FLAG_PULLUP) && (flags & gpio::FLAG_PULLDOWN)) { |     if (flags & gpio::FLAG_OUTPUT) { | ||||||
|     pull_mode = GPIO_PULLUP_PULLDOWN; |       gpio_hal_output_enable(&GPIO_HAL, arg->pin); | ||||||
|   } else if (flags & gpio::FLAG_PULLUP) { |       if (flags & gpio::FLAG_OPEN_DRAIN) | ||||||
|     pull_mode = GPIO_PULLUP_ONLY; |         gpio_hal_od_enable(&GPIO_HAL, arg->pin); | ||||||
|   } else if (flags & gpio::FLAG_PULLDOWN) { |     } else { | ||||||
|     pull_mode = GPIO_PULLDOWN_ONLY; |       gpio_hal_output_disable(&GPIO_HAL, arg->pin); | ||||||
|     } |     } | ||||||
|   gpio_set_pull_mode(arg->pin, pull_mode); |   } | ||||||
|  |   if (diff & gpio::FLAG_INPUT) { | ||||||
|  |     if (flags & gpio::FLAG_INPUT) { | ||||||
|  |       gpio_hal_input_enable(&GPIO_HAL, arg->pin); | ||||||
|  | #if defined(USE_ESP32_VARIANT_ESP32) | ||||||
|  |       if (arg->use_rtc) { | ||||||
|  |         if (flags & gpio::FLAG_PULLUP) { | ||||||
|  |           rtcio_hal_pullup_enable(arg->rtc_pin); | ||||||
|  |         } else { | ||||||
|  |           rtcio_hal_pullup_disable(arg->rtc_pin); | ||||||
|  |         } | ||||||
|  |         if (flags & gpio::FLAG_PULLDOWN) { | ||||||
|  |           rtcio_hal_pulldown_enable(arg->rtc_pin); | ||||||
|  |         } else { | ||||||
|  |           rtcio_hal_pulldown_disable(arg->rtc_pin); | ||||||
|  |         } | ||||||
|  |       } else | ||||||
|  | #endif | ||||||
|  |       { | ||||||
|  |         if (flags & gpio::FLAG_PULLUP) { | ||||||
|  |           gpio_hal_pullup_en(&GPIO_HAL, arg->pin); | ||||||
|  |         } else { | ||||||
|  |           gpio_hal_pullup_dis(&GPIO_HAL, arg->pin); | ||||||
|  |         } | ||||||
|  |         if (flags & gpio::FLAG_PULLDOWN) { | ||||||
|  |           gpio_hal_pulldown_en(&GPIO_HAL, arg->pin); | ||||||
|  |         } else { | ||||||
|  |           gpio_hal_pulldown_dis(&GPIO_HAL, arg->pin); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       gpio_hal_input_disable(&GPIO_HAL, arg->pin); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   arg->flags = flags; | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ namespace esp8266 { | |||||||
|  |  | ||||||
| static const char *const TAG = "esp8266"; | static const char *const TAG = "esp8266"; | ||||||
|  |  | ||||||
| static int IRAM_ATTR flags_to_mode(gpio::Flags flags, uint8_t pin) { | static int flags_to_mode(gpio::Flags flags, uint8_t pin) { | ||||||
|   if (flags == gpio::FLAG_INPUT) {  // NOLINT(bugprone-branch-clone) |   if (flags == gpio::FLAG_INPUT) {  // NOLINT(bugprone-branch-clone) | ||||||
|     return INPUT; |     return INPUT; | ||||||
|   } else if (flags == gpio::FLAG_OUTPUT) { |   } else if (flags == gpio::FLAG_OUTPUT) { | ||||||
| @@ -34,12 +34,36 @@ static int IRAM_ATTR flags_to_mode(gpio::Flags flags, uint8_t pin) { | |||||||
| struct ISRPinArg { | struct ISRPinArg { | ||||||
|   uint8_t pin; |   uint8_t pin; | ||||||
|   bool inverted; |   bool inverted; | ||||||
|  |   volatile uint32_t *in_reg; | ||||||
|  |   volatile uint32_t *out_set_reg; | ||||||
|  |   volatile uint32_t *out_clr_reg; | ||||||
|  |   volatile uint32_t *mode_set_reg; | ||||||
|  |   volatile uint32_t *mode_clr_reg; | ||||||
|  |   volatile uint32_t *func_reg; | ||||||
|  |   uint32_t mask; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| ISRInternalGPIOPin ESP8266GPIOPin::to_isr() const { | ISRInternalGPIOPin ESP8266GPIOPin::to_isr() const { | ||||||
|   auto *arg = new ISRPinArg{};  // NOLINT(cppcoreguidelines-owning-memory) |   auto *arg = new ISRPinArg{};  // NOLINT(cppcoreguidelines-owning-memory) | ||||||
|   arg->pin = pin_; |   arg->pin = this->pin_; | ||||||
|   arg->inverted = inverted_; |   arg->inverted = this->inverted_; | ||||||
|  |   if (this->pin_ < 16) { | ||||||
|  |     arg->in_reg = &GPI; | ||||||
|  |     arg->out_set_reg = &GPOS; | ||||||
|  |     arg->out_clr_reg = &GPOC; | ||||||
|  |     arg->mode_set_reg = &GPES; | ||||||
|  |     arg->mode_clr_reg = &GPEC; | ||||||
|  |     arg->func_reg = &GPF(this->pin_); | ||||||
|  |     arg->mask = 1 << this->pin_; | ||||||
|  |   } else { | ||||||
|  |     arg->in_reg = &GP16I; | ||||||
|  |     arg->out_set_reg = &GP16O; | ||||||
|  |     arg->out_clr_reg = nullptr; | ||||||
|  |     arg->mode_set_reg = &GP16E; | ||||||
|  |     arg->mode_clr_reg = nullptr; | ||||||
|  |     arg->func_reg = &GPF16; | ||||||
|  |     arg->mask = 1; | ||||||
|  |   } | ||||||
|   return ISRInternalGPIOPin((void *) arg); |   return ISRInternalGPIOPin((void *) arg); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -88,20 +112,57 @@ void ESP8266GPIOPin::detach_interrupt() const { detachInterrupt(pin_); } | |||||||
| using namespace esp8266; | using namespace esp8266; | ||||||
|  |  | ||||||
| bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { | bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { | ||||||
|   auto *arg = reinterpret_cast<ISRPinArg *>(arg_); |   auto *arg = reinterpret_cast<ISRPinArg *>(this->arg_); | ||||||
|   return bool(digitalRead(arg->pin)) != arg->inverted;  // NOLINT |   return bool(*arg->in_reg & arg->mask) != arg->inverted; | ||||||
| } | } | ||||||
|  |  | ||||||
| void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) { | void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) { | ||||||
|   auto *arg = reinterpret_cast<ISRPinArg *>(arg_); |   auto *arg = reinterpret_cast<ISRPinArg *>(arg_); | ||||||
|   digitalWrite(arg->pin, value != arg->inverted ? 1 : 0);  // NOLINT |   if (arg->pin < 16) { | ||||||
|  |     if (value != arg->inverted) { | ||||||
|  |       *arg->out_set_reg = arg->mask; | ||||||
|  |     } else { | ||||||
|  |       *arg->out_clr_reg = arg->mask; | ||||||
|     } |     } | ||||||
|  |   } else { | ||||||
|  |     if (value != arg->inverted) { | ||||||
|  |       *arg->out_set_reg |= 1; | ||||||
|  |     } else { | ||||||
|  |       *arg->out_set_reg &= ~1; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() { | void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() { | ||||||
|   auto *arg = reinterpret_cast<ISRPinArg *>(arg_); |   auto *arg = reinterpret_cast<ISRPinArg *>(arg_); | ||||||
|   GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1UL << arg->pin); |   GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1UL << arg->pin); | ||||||
| } | } | ||||||
|  |  | ||||||
| void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) { | void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) { | ||||||
|   auto *arg = reinterpret_cast<ISRPinArg *>(arg_); |   auto *arg = reinterpret_cast<ISRPinArg *>(this->arg_); | ||||||
|   pinMode(arg->pin, flags_to_mode(flags, arg->pin));  // NOLINT |   if (arg->pin < 16) { | ||||||
|  |     if (flags & gpio::FLAG_OUTPUT) { | ||||||
|  |       *arg->mode_set_reg = arg->mask; | ||||||
|  |     } else { | ||||||
|  |       *arg->mode_clr_reg = arg->mask; | ||||||
|  |     } | ||||||
|  |     if (flags & gpio::FLAG_PULLUP) { | ||||||
|  |       *arg->func_reg |= 1 << GPFPU; | ||||||
|  |     } else { | ||||||
|  |       *arg->func_reg &= ~(1 << GPFPU); | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     if (flags & gpio::FLAG_OUTPUT) { | ||||||
|  |       *arg->mode_set_reg |= 1; | ||||||
|  |     } else { | ||||||
|  |       *arg->mode_set_reg &= ~1; | ||||||
|  |     } | ||||||
|  |     if (flags & gpio::FLAG_PULLDOWN) { | ||||||
|  |       *arg->func_reg |= 1 << GP16FPD; | ||||||
|  |     } else { | ||||||
|  |       *arg->func_reg &= ~(1 << GP16FPD); | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user