mirror of
https://github.com/esphome/esphome.git
synced 2026-06-24 14:01:01 +00:00
[sx1509] Add interrupt pin support
Add optional interrupt_pin configuration to eliminate I2C polling for GPIO reads. When configured, the component disables its loop and only reads the I2C bus when the interrupt fires, matching the pattern used by PCF8574, PCA9554, and other GPIO expanders. When keypad mode is active, the loop continues running for key scanning regardless of interrupt configuration.
This commit is contained in:
@@ -5,6 +5,7 @@ import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_INPUT,
|
||||
CONF_INTERRUPT_PIN,
|
||||
CONF_INVERTED,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
@@ -75,6 +76,7 @@ CONFIG_SCHEMA = (
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(SX1509Component),
|
||||
cv.Optional(CONF_KEYPAD): cv.Schema(KEYPAD_SCHEMA),
|
||||
cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
@@ -86,6 +88,8 @@ async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
if interrupt_pin := config.get(CONF_INTERRUPT_PIN):
|
||||
cg.add(var.set_interrupt_pin(await cg.gpio_pin_expression(interrupt_pin)))
|
||||
if conf := config.get(CONF_KEYPAD):
|
||||
cg.add(var.set_rows_cols(conf[CONF_KEY_ROWS], conf[CONF_KEY_COLUMNS]))
|
||||
if (
|
||||
|
||||
@@ -28,10 +28,23 @@ void SX1509Component::setup() {
|
||||
delayMicroseconds(500);
|
||||
if (this->has_keypad_)
|
||||
this->setup_keypad_();
|
||||
|
||||
if (this->interrupt_pin_ != nullptr) {
|
||||
this->interrupt_pin_->setup();
|
||||
this->interrupt_pin_->attach_interrupt(&SX1509Component::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE);
|
||||
this->set_invalidate_on_read_(false);
|
||||
}
|
||||
// Disable loop until an input pin is configured via pin_mode()
|
||||
// or keypad is active. For interrupt-driven mode, loop is re-enabled by the ISR.
|
||||
if (!this->has_keypad_) {
|
||||
this->disable_loop();
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR SX1509Component::gpio_intr(SX1509Component *arg) { arg->enable_loop_soon_any_context(); }
|
||||
void SX1509Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "SX1509:");
|
||||
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Setting up SX1509 failed!");
|
||||
}
|
||||
@@ -41,6 +54,9 @@ void SX1509Component::dump_config() {
|
||||
void SX1509Component::loop() {
|
||||
// Reset cache at the start of each loop
|
||||
this->reset_pin_cache_();
|
||||
if (this->interrupt_pin_ != nullptr && !this->has_keypad_) {
|
||||
this->disable_loop();
|
||||
}
|
||||
|
||||
if (this->has_keypad_) {
|
||||
if (millis() - this->last_loop_timestamp_ < min_loop_period_)
|
||||
@@ -169,6 +185,9 @@ void SX1509Component::pin_mode(uint8_t pin, gpio::Flags flags) {
|
||||
// Set direction to input
|
||||
this->ddr_mask_ |= (1 << pin);
|
||||
this->write_byte_16(REG_DIR_B, this->ddr_mask_);
|
||||
if (this->interrupt_pin_ == nullptr) {
|
||||
this->enable_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ class SX1509Component : public Component,
|
||||
uint16_t read_key_data();
|
||||
void set_pin_value(uint8_t pin, uint8_t i_on) { this->write_byte(REG_I_ON[pin], i_on); };
|
||||
void pin_mode(uint8_t pin, gpio::Flags flags);
|
||||
void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; }
|
||||
uint32_t get_clock() { return this->clk_x_; };
|
||||
void set_rows_cols(uint8_t rows, uint8_t cols) {
|
||||
this->rows_ = rows;
|
||||
@@ -63,6 +64,8 @@ class SX1509Component : public Component,
|
||||
void setup_led_driver(uint8_t pin);
|
||||
|
||||
protected:
|
||||
static void IRAM_ATTR gpio_intr(SX1509Component *arg);
|
||||
|
||||
// Virtual methods from CachedGpioExpander
|
||||
bool digital_read_hw(uint8_t pin) override;
|
||||
bool digital_read_cache(uint8_t pin) override;
|
||||
@@ -85,6 +88,7 @@ class SX1509Component : public Component,
|
||||
std::vector<SX1509Processor *> keypad_binary_sensors_;
|
||||
std::vector<SX1509KeyTrigger *> key_triggers_;
|
||||
|
||||
InternalGPIOPin *interrupt_pin_{nullptr};
|
||||
uint32_t last_loop_timestamp_ = 0;
|
||||
const uint32_t min_loop_period_ = 15; // ms
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ sx1509:
|
||||
- id: sx1509_hub
|
||||
i2c_id: i2c_bus
|
||||
address: 0x3E
|
||||
interrupt_pin: ${interrupt_pin}
|
||||
keypad:
|
||||
key_rows: 2
|
||||
key_columns: 2
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
substitutions:
|
||||
interrupt_pin: GPIO15
|
||||
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
substitutions:
|
||||
interrupt_pin: GPIO15
|
||||
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
substitutions:
|
||||
interrupt_pin: GPIO2
|
||||
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml
|
||||
|
||||
|
||||
Reference in New Issue
Block a user