mirror of
https://github.com/esphome/esphome.git
synced 2026-06-24 16:56:44 +00:00
Compare commits
11 Commits
scheduler-
...
sx1509-int
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32f05f50f5 | ||
|
|
37796887f0 | ||
|
|
dd2543a2d6 | ||
|
|
dce6d5ab0c | ||
|
|
867df4225a | ||
|
|
801f385896 | ||
|
|
010717afa2 | ||
|
|
7841ef7067 | ||
|
|
9691d5b381 | ||
|
|
043d05cd12 | ||
|
|
59065c71a8 |
@@ -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,26 @@ 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->interrupt_pending_ = true;
|
||||
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!");
|
||||
}
|
||||
@@ -39,7 +55,17 @@ void SX1509Component::dump_config() {
|
||||
}
|
||||
|
||||
void SX1509Component::loop() {
|
||||
// Reset cache at the start of each loop
|
||||
if (this->interrupt_pending_) {
|
||||
this->interrupt_pending_ = false;
|
||||
// Clear interrupt source before resetting cache to avoid losing
|
||||
// pin changes that occur between cache reset and interrupt clear
|
||||
this->clear_interrupt_();
|
||||
this->reset_pin_cache_();
|
||||
if (!this->has_keypad_) {
|
||||
this->disable_loop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
this->reset_pin_cache_();
|
||||
|
||||
if (this->has_keypad_) {
|
||||
@@ -169,6 +195,11 @@ 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_pin_interrupt_(pin);
|
||||
} else {
|
||||
this->enable_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,5 +344,23 @@ void SX1509Component::set_debounce_keypad_(uint8_t time, uint8_t num_rows, uint8
|
||||
set_debounce_pin_(i + 8);
|
||||
}
|
||||
|
||||
void SX1509Component::enable_pin_interrupt_(uint8_t pin) {
|
||||
// Unmask this pin's interrupt (clear bit = enabled)
|
||||
uint16_t mask = 0;
|
||||
this->read_byte_16(REG_INTERRUPT_MASK_B, &mask);
|
||||
mask &= ~(1 << pin);
|
||||
this->write_byte_16(REG_INTERRUPT_MASK_B, mask);
|
||||
|
||||
// Configure sense to trigger on both edges
|
||||
uint8_t sense_reg = SENSE_REGS[pin / 4];
|
||||
uint8_t sense_val = 0;
|
||||
this->read_byte(sense_reg, &sense_val);
|
||||
// 2-bit field position within the sense register (4 pins per register, 2 bits each)
|
||||
uint8_t shift = (pin % 4) * 2;
|
||||
sense_val &= ~(SENSE_BOTH_EDGES << shift);
|
||||
sense_val |= (SENSE_BOTH_EDGES << shift);
|
||||
this->write_byte(sense_reg, sense_val);
|
||||
}
|
||||
|
||||
} // namespace sx1509
|
||||
} // namespace esphome
|
||||
|
||||
@@ -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;
|
||||
@@ -75,6 +78,7 @@ class SX1509Component : public Component,
|
||||
uint16_t port_mask_ = 0x00;
|
||||
uint16_t output_state_ = 0x00;
|
||||
bool has_keypad_ = false;
|
||||
volatile bool interrupt_pending_{false};
|
||||
uint8_t rows_ = 0;
|
||||
uint8_t cols_ = 0;
|
||||
std::string keys_;
|
||||
@@ -85,9 +89,15 @@ 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
|
||||
|
||||
void enable_pin_interrupt_(uint8_t pin);
|
||||
void clear_interrupt_() {
|
||||
uint16_t interrupt_source = 0;
|
||||
this->read_byte_16(REG_INTERRUPT_SOURCE_B, &interrupt_source);
|
||||
}
|
||||
void setup_keypad_();
|
||||
void set_debounce_config_(uint8_t config_value);
|
||||
void set_debounce_time_(uint8_t time);
|
||||
|
||||
@@ -57,6 +57,10 @@ const uint8_t REG_SENSE_HIGH_B = 0x14; // RegSenseHighB Sense register for I
|
||||
const uint8_t REG_SENSE_LOW_B = 0x15; // RegSenseLowB Sense register for I/O[11:8] 0000 0000
|
||||
const uint8_t REG_SENSE_HIGH_A = 0x16; // RegSenseHighA Sense register for I/O[7:4] 0000 0000
|
||||
const uint8_t REG_SENSE_LOW_A = 0x17; // RegSenseLowA Sense register for I/O[3:0] 0000 0000
|
||||
// Sense register values (2 bits per pin): 00=None, 01=Rising, 10=Falling, 11=Both
|
||||
const uint8_t SENSE_BOTH_EDGES = 0x03;
|
||||
// Sense register lookup: each register covers 4 pins (pins 0-3, 4-7, 8-11, 12-15)
|
||||
const uint8_t SENSE_REGS[] = {REG_SENSE_LOW_A, REG_SENSE_HIGH_A, REG_SENSE_LOW_B, REG_SENSE_HIGH_B};
|
||||
const uint8_t REG_INTERRUPT_SOURCE_B =
|
||||
0x18; // RegInterruptSourceB Interrupt source register _ I/O[15_8] (Bank B) 0000 0000
|
||||
const uint8_t REG_INTERRUPT_SOURCE_A =
|
||||
|
||||
@@ -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