TCA6408 Updates

This commit is contained in:
Dave Williams | DitroniX | G8PUO
2026-06-13 08:28:59 +01:00
parent d52e1874cb
commit 4215d0022f
3 changed files with 55 additions and 34 deletions
+1 -3
View File
@@ -13,9 +13,7 @@ CONFIG_SCHEMA = (
{ {
cv.GenerateID(): cv.declare_id(TCA6408Component), cv.GenerateID(): cv.declare_id(TCA6408Component),
cv.Optional(CONF_ADDRESS, default=0x20): cv.i2c_address, cv.Optional(CONF_ADDRESS, default=0x20): cv.i2c_address,
cv.Optional(CONF_INTERRUPT_PIN): cv.All( cv.Optional(CONF_INTERRUPT_PIN): gpio.validate_gpio_pin("internal"),
gpio.validate_gpio_pin("internal"),
),
} }
) )
.extend(cv.COMPONENT_SCHEMA) .extend(cv.COMPONENT_SCHEMA)
+47 -27
View File
@@ -12,11 +12,23 @@ static const uint8_t TCA6408_POLARITY_PORT = 0x02;
static const uint8_t TCA6408_CONFIG_PORT = 0x03; static const uint8_t TCA6408_CONFIG_PORT = 0x03;
void TCA6408Component::setup() { void TCA6408Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up TCA6408..."); ESP_LOGCONFIG(TAG, "Setting up TCA6408 at address 0x%02X...", this->address_);
if (this->write_register(TCA6408_POLARITY_PORT, 0x00) != i2c::ERROR_OK || // Initialize registers to known state
this->write_register(TCA6408_CONFIG_PORT, this->mode_mask_) != i2c::ERROR_OK || if (this->write_register(TCA6408_POLARITY_PORT, 0x00) != i2c::ERROR_OK) {
!this->read_gpio_outputs_()) { ESP_LOGE(TAG, "Failed to write polarity register");
this->mark_failed();
return;
}
if (this->write_register(TCA6408_CONFIG_PORT, this->mode_mask_) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Failed to write config register");
this->mark_failed();
return;
}
if (!this->read_gpio_outputs_()) {
ESP_LOGE(TAG, "Failed to read output register");
this->mark_failed(); this->mark_failed();
return; return;
} }
@@ -24,13 +36,16 @@ void TCA6408Component::setup() {
if (this->interrupt_pin_ != nullptr) { if (this->interrupt_pin_ != nullptr) {
this->interrupt_pin_->setup(); this->interrupt_pin_->setup();
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
this->interrupt_pin_->attach_interrupt(&TCA6408Component::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE); this->interrupt_pin_->attach_interrupt(&TCA6408Component::gpio_intr, this,
gpio::INTERRUPT_FALLING_EDGE);
this->set_invalidate_on_read_(false); this->set_invalidate_on_read_(false);
ESP_LOGCONFIG(TAG, " Interrupt mode enabled"); ESP_LOGCONFIG(TAG, " Interrupt mode enabled (active-low INT)");
} else { } else {
this->enable_loop(); this->enable_loop();
ESP_LOGCONFIG(TAG, " Polling mode enabled"); ESP_LOGCONFIG(TAG, " Polling mode enabled");
} }
ESP_LOGCONFIG(TAG, " Setup complete");
} }
void IRAM_ATTR TCA6408Component::gpio_intr(TCA6408Component *arg) { void IRAM_ATTR TCA6408Component::gpio_intr(TCA6408Component *arg) {
@@ -42,9 +57,11 @@ void TCA6408Component::loop() {
if (this->interrupt_triggered_) { if (this->interrupt_triggered_) {
this->interrupt_triggered_ = false; this->interrupt_triggered_ = false;
this->digital_read_hw(0); // Reading input port clears the interrupt on TCA6408
this->digital_read_hw(0); // pin number is ignored, reads all 8 bits
this->reset_pin_cache_(); this->reset_pin_cache_();
// If INT line is high again, disable loop until next interrupt
if (this->interrupt_pin_ && this->interrupt_pin_->digital_read()) { if (this->interrupt_pin_ && this->interrupt_pin_->digital_read()) {
this->disable_loop(); this->disable_loop();
} }
@@ -56,11 +73,13 @@ void TCA6408Component::dump_config() {
LOG_I2C_DEVICE(this); LOG_I2C_DEVICE(this);
ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
ESP_LOGCONFIG(TAG, " Mode Mask: 0x%02X (1=input)", this->mode_mask_);
ESP_LOGCONFIG(TAG, " Polarity Mask: 0x%02X", this->polarity_mask_);
} }
bool TCA6408Component::read_gpio_outputs_() { bool TCA6408Component::read_gpio_outputs_() {
if (this->is_failed()) return false; if (this->is_failed()) return false;
uint8_t data; uint8_t data = 0;
if (this->read_register(TCA6408_OUTPUT_PORT, &data) != i2c::ERROR_OK) { if (this->read_register(TCA6408_OUTPUT_PORT, &data) != i2c::ERROR_OK) {
this->status_set_warning(); this->status_set_warning();
return false; return false;
@@ -87,9 +106,9 @@ void TCA6408Component::digital_write_hw(uint8_t pin, bool value) {
if (this->is_failed()) return; if (this->is_failed()) return;
if (value) { if (value) {
this->output_mask_ |= (1 << pin); this->output_mask_ |= (1U << pin);
} else { } else {
this->output_mask_ &= ~(1 << pin); this->output_mask_ &= ~(1U << pin);
} }
if (this->write_register(TCA6408_OUTPUT_PORT, this->output_mask_) != i2c::ERROR_OK) { if (this->write_register(TCA6408_OUTPUT_PORT, this->output_mask_) != i2c::ERROR_OK) {
@@ -101,9 +120,9 @@ void TCA6408Component::digital_write_hw(uint8_t pin, bool value) {
void TCA6408Component::pin_mode(uint8_t pin, gpio::Flags flags) { void TCA6408Component::pin_mode(uint8_t pin, gpio::Flags flags) {
if (flags & gpio::FLAG_OUTPUT) { if (flags & gpio::FLAG_OUTPUT) {
this->mode_mask_ &= ~(1 << pin); this->mode_mask_ &= ~(1U << pin); // 0 = output
} else { } else {
this->mode_mask_ |= (1 << pin); this->mode_mask_ |= (1U << pin); // 1 = input
if (this->interrupt_pin_ == nullptr) { if (this->interrupt_pin_ == nullptr) {
this->enable_loop(); this->enable_loop();
} }
@@ -111,24 +130,25 @@ void TCA6408Component::pin_mode(uint8_t pin, gpio::Flags flags) {
this->write_register(TCA6408_CONFIG_PORT, this->mode_mask_); this->write_register(TCA6408_CONFIG_PORT, this->mode_mask_);
} }
void TCA6408Component::set_polarity_inversion(uint8_t pin, bool inverted) {
if (inverted) {
this->polarity_mask_ |= (1U << pin);
} else {
this->polarity_mask_ &= ~(1U << pin);
}
this->write_register(TCA6408_POLARITY_PORT, this->polarity_mask_);
}
bool TCA6408Component::digital_read_cache(uint8_t pin) { bool TCA6408Component::digital_read_cache(uint8_t pin) {
bool value = (this->input_mask_ & (1 << pin)) != 0; bool value = (this->input_mask_ & (1U << pin)) != 0;
if (this->polarity_mask_ & (1 << pin)) { // Apply hardware polarity inversion if set
if (this->polarity_mask_ & (1U << pin)) {
value = !value; value = !value;
} }
return value; return value;
} }
void TCA6408Component::set_polarity_inversion(uint8_t pin, bool inverted) { // ====================== TCA6408GPIOPin ======================
if (inverted) {
this->polarity_mask_ |= (1 << pin);
} else {
this->polarity_mask_ &= ~(1 << pin);
}
this->write_register(TCA6408_POLARITY_PORT, this->polarity_mask_);
}
// ==================== TCA6408GPIOPin ====================
void TCA6408GPIOPin::setup() { void TCA6408GPIOPin::setup() {
this->pin_mode(this->flags_); this->pin_mode(this->flags_);
@@ -150,15 +170,15 @@ void TCA6408GPIOPin::pin_mode(gpio::Flags flags) {
} }
bool TCA6408GPIOPin::digital_read() { bool TCA6408GPIOPin::digital_read() {
return this->parent_->digital_read(this->pin_); return this->parent_->digital_read(this->pin_) != this->inverted_;
} }
void TCA6408GPIOPin::digital_write(bool value) { void TCA6408GPIOPin::digital_write(bool value) {
this->parent_->digital_write(this->pin_, value); this->parent_->digital_write(this->pin_, value != this->inverted_);
} }
size_t TCA6408GPIOPin::dump_summary(char *buffer, size_t len) const { size_t TCA6408GPIOPin::dump_summary(char *buffer, size_t len) const {
return snprintf(buffer, len, "%u via TCA6408", this->pin_); return snprintf(buffer, len, "%u via TCA6408@0x%02X", this->pin_, this->parent_->address_);
} }
} // namespace tca6408 } // namespace tca6408
+4 -1
View File
@@ -33,10 +33,13 @@ class TCA6408Component : public Component,
uint8_t mode_mask_{0xFF}; // 1 = input, 0 = output uint8_t mode_mask_{0xFF}; // 1 = input, 0 = output
uint8_t output_mask_{0x00}; uint8_t output_mask_{0x00};
uint8_t input_mask_{0x00}; uint8_t input_mask_{0x00};
uint8_t polarity_mask_{0x00}; // 1 = inverted uint8_t polarity_mask_{0x00}; // 1 = inverted (for inputs)
InternalGPIOPin *interrupt_pin_{nullptr}; InternalGPIOPin *interrupt_pin_{nullptr};
bool interrupt_triggered_{false}; bool interrupt_triggered_{false};
private:
bool read_gpio_outputs_();
}; };
class TCA6408GPIOPin : public gpio::GPIOPin, public Parented<TCA6408Component> { class TCA6408GPIOPin : public gpio::GPIOPin, public Parented<TCA6408Component> {