Refactor TCA6408 component and update register names
Refactor TCA6408 component to improve readability and maintainability. Update register names and logging messages for consistency.
This commit is contained in:
committed by
GitHub
parent
638eaa4867
commit
85f02c972e
+78
-102
@@ -6,74 +6,68 @@ namespace tca6408 {
|
|||||||
|
|
||||||
static const char *const TAG = "tca6408";
|
static const char *const TAG = "tca6408";
|
||||||
|
|
||||||
// TCA6408 Register Addresses (from datasheet)
|
// Registers
|
||||||
static const uint8_t TCA6408_INPUT_PORT = 0x00; // Input Port Register
|
static const uint8_t REG_INPUT = 0x00;
|
||||||
static const uint8_t TCA6408_OUTPUT_PORT = 0x01; // Output Port Register
|
static const uint8_t REG_OUTPUT = 0x01;
|
||||||
static const uint8_t TCA6408_POLARITY_PORT = 0x02; // Polarity Inversion Register
|
static const uint8_t REG_POLARITY = 0x02;
|
||||||
static const uint8_t TCA6408_CONFIG_PORT = 0x03; // Configuration Register (1 = input, 0 = output)
|
static const uint8_t REG_CONFIG = 0x03;
|
||||||
|
|
||||||
void TCA6408Component::setup() {
|
void TCA6408Component::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up TCA6408 at address 0x%02X...", this->address_);
|
ESP_LOGCONFIG(TAG, "Setting up TCA6408 @ 0x%02X", this->address_);
|
||||||
|
|
||||||
// Initialize chip to a known safe state
|
// Default safe state: all inputs
|
||||||
if (this->write_register(TCA6408_POLARITY_PORT, 0x00) != i2c::ERROR_OK) {
|
this->mode_mask_ = 0xFF;
|
||||||
ESP_LOGE(TAG, "Failed to write polarity register");
|
this->output_mask_ = 0x00;
|
||||||
|
this->input_mask_ = 0x00;
|
||||||
|
this->polarity_mask_ = 0x00;
|
||||||
|
|
||||||
|
if (this->write_register(REG_POLARITY, 0x00) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to init polarity register");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->write_register(TCA6408_CONFIG_PORT, this->mode_mask_) != i2c::ERROR_OK) {
|
if (this->write_register(REG_CONFIG, this->mode_mask_) != i2c::ERROR_OK) {
|
||||||
ESP_LOGE(TAG, "Failed to write config register");
|
ESP_LOGE(TAG, "Failed to init config register");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->read_gpio_outputs_()) {
|
this->read_inputs_();
|
||||||
ESP_LOGE(TAG, "Failed to read initial output state");
|
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure interrupt mode if requested
|
|
||||||
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); // Required for open-drain INT
|
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
|
||||||
this->interrupt_pin_->attach_interrupt(&TCA6408Component::gpio_intr, this,
|
this->interrupt_pin_->attach_interrupt(
|
||||||
|
&TCA6408Component::gpio_intr,
|
||||||
|
this,
|
||||||
gpio::INTERRUPT_FALLING_EDGE);
|
gpio::INTERRUPT_FALLING_EDGE);
|
||||||
this->set_invalidate_on_read_(false); // We manage cache manually for efficiency
|
|
||||||
ESP_LOGCONFIG(TAG, " Interrupt mode enabled (active-low INT pin)");
|
this->enable_loop();
|
||||||
|
ESP_LOGCONFIG(TAG, "Interrupt mode enabled");
|
||||||
} else {
|
} else {
|
||||||
this->enable_loop(); // Fallback to polling
|
this->enable_loop();
|
||||||
ESP_LOGCONFIG(TAG, "Polling mode enabled");
|
ESP_LOGCONFIG(TAG, "Polling mode enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGCONFIG(TAG, "TCA6408 setup complete");
|
ESP_LOGCONFIG(TAG, "TCA6408 setup complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Interrupt Service Routine (ISR)
|
|
||||||
* Runs in interrupt context - must be very lightweight
|
|
||||||
*/
|
|
||||||
void IRAM_ATTR TCA6408Component::gpio_intr(TCA6408Component *arg) {
|
void IRAM_ATTR TCA6408Component::gpio_intr(TCA6408Component *arg) {
|
||||||
arg->interrupt_triggered_ = true;
|
arg->interrupt_triggered_ = true;
|
||||||
arg->enable_loop_soon_any_context(); // Schedule loop() to run soon
|
arg->enable_loop_soon_any_context();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Main loop - handles interrupt processing
|
|
||||||
* Only runs when an interrupt occurs (in interrupt mode)
|
|
||||||
*/
|
|
||||||
void TCA6408Component::loop() {
|
void TCA6408Component::loop() {
|
||||||
if (this->interrupt_triggered_) {
|
if (this->interrupt_triggered_) {
|
||||||
this->interrupt_triggered_ = false;
|
this->interrupt_triggered_ = false;
|
||||||
|
|
||||||
// Reading Input Port automatically clears the INT pin on TCA6408
|
this->read_inputs_();
|
||||||
this->digital_read_hw(0); // pin argument is ignored - reads all 8 bits
|
this->reset_pin_cache_();
|
||||||
this->reset_pin_cache_(); // Notify ESPHome that inputs may have changed
|
|
||||||
|
|
||||||
// Check if INT line has returned high (no more pending interrupts)
|
|
||||||
if (this->interrupt_pin_ && this->interrupt_pin_->digital_read()) {
|
if (this->interrupt_pin_ && this->interrupt_pin_->digital_read()) {
|
||||||
this->disable_loop(); // Stop looping until next interrupt
|
// INT released -> stop looping until next interrupt
|
||||||
|
this->disable_loop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,29 +76,13 @@ void TCA6408Component::dump_config() {
|
|||||||
ESP_LOGCONFIG(TAG, "TCA6408:");
|
ESP_LOGCONFIG(TAG, "TCA6408:");
|
||||||
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(" INT 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_inputs_() {
|
||||||
if (this->is_failed()) return false;
|
|
||||||
uint8_t data = 0;
|
uint8_t data = 0;
|
||||||
if (this->read_register(TCA6408_OUTPUT_PORT, &data) != i2c::ERROR_OK) {
|
|
||||||
this->status_set_warning();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this->output_mask_ = data;
|
|
||||||
this->status_clear_warning();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Read current state of all input pins from hardware */
|
if (this->read_register(REG_INPUT, &data) != i2c::ERROR_OK) {
|
||||||
bool TCA6408Component::digital_read_hw(uint8_t pin) {
|
|
||||||
if (this->is_failed()) return false;
|
|
||||||
|
|
||||||
uint8_t data = 0;
|
|
||||||
if (this->read_register(TCA6408_INPUT_PORT, &data) != i2c::ERROR_OK) {
|
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -114,71 +92,65 @@ bool TCA6408Component::digital_read_hw(uint8_t pin) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Write to a specific output pin */
|
bool TCA6408Component::digital_read_hw(uint8_t pin) {
|
||||||
void TCA6408Component::digital_write_hw(uint8_t pin, bool value) {
|
// full refresh for accuracy
|
||||||
if (this->is_failed()) return;
|
return this->read_inputs_();
|
||||||
|
|
||||||
if (value) {
|
|
||||||
this->output_mask_ |= (1U << pin);
|
|
||||||
} else {
|
|
||||||
this->output_mask_ &= ~(1U << pin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->write_register(TCA6408_OUTPUT_PORT, this->output_mask_) != i2c::ERROR_OK) {
|
bool TCA6408Component::digital_read_cache(uint8_t pin) {
|
||||||
|
bool value = (this->input_mask_ >> pin) & 0x01;
|
||||||
|
|
||||||
|
if (this->polarity_mask_ & (1 << pin)) {
|
||||||
|
value = !value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCA6408Component::digital_write_hw(uint8_t pin, bool value) {
|
||||||
|
if (value)
|
||||||
|
this->output_mask_ |= (1 << pin);
|
||||||
|
else
|
||||||
|
this->output_mask_ &= ~(1 << pin);
|
||||||
|
|
||||||
|
if (this->write_register(REG_OUTPUT, this->output_mask_) != i2c::ERROR_OK) {
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
} else {
|
} else {
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Configure pin direction (input/output) */
|
|
||||||
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_ &= ~(1U << pin); // 0 = output
|
this->mode_mask_ &= ~(1 << pin);
|
||||||
} else {
|
} else {
|
||||||
this->mode_mask_ |= (1U << pin); // 1 = input
|
this->mode_mask_ |= (1 << pin);
|
||||||
if (this->interrupt_pin_ == nullptr) {
|
}
|
||||||
this->enable_loop(); // Enable polling if no interrupt pin
|
|
||||||
}
|
this->write_register(REG_CONFIG, this->mode_mask_);
|
||||||
}
|
|
||||||
this->write_register(TCA6408_CONFIG_PORT, this->mode_mask_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set hardware polarity inversion for inputs */
|
|
||||||
void TCA6408Component::set_polarity_inversion(uint8_t pin, bool inverted) {
|
void TCA6408Component::set_polarity_inversion(uint8_t pin, bool inverted) {
|
||||||
if (inverted) {
|
if (inverted)
|
||||||
this->polarity_mask_ |= (1U << pin);
|
this->polarity_mask_ |= (1 << pin);
|
||||||
} else {
|
else
|
||||||
this->polarity_mask_ &= ~(1U << pin);
|
this->polarity_mask_ &= ~(1 << pin);
|
||||||
}
|
|
||||||
this->write_register(TCA6408_POLARITY_PORT, this->polarity_mask_);
|
this->write_register(REG_POLARITY, this->polarity_mask_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Read cached value with polarity inversion applied */
|
// ================= GPIO PIN WRAPPER =================
|
||||||
bool TCA6408Component::digital_read_cache(uint8_t pin) {
|
|
||||||
bool value = (this->input_mask_ & (1U << pin)) != 0;
|
|
||||||
|
|
||||||
// Apply hardware polarity inversion if enabled for this pin
|
void TCA6408GPIOPin::set_parent(TCA6408Component *parent) {
|
||||||
if (this->polarity_mask_ & (1U << pin)) {
|
this->parent_ = parent;
|
||||||
value = !value;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================== TCA6408GPIOPin Wrapper ======================
|
void TCA6408GPIOPin::set_pin(uint8_t pin) {
|
||||||
|
this->pin_ = pin;
|
||||||
|
}
|
||||||
|
|
||||||
void TCA6408GPIOPin::setup() {
|
void TCA6408GPIOPin::setup() {
|
||||||
this->pin_mode(this->flags_);
|
this->pin_mode(this->flags_);
|
||||||
if (this->inverted_) {
|
|
||||||
this->parent_->set_polarity_inversion(this->pin_, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCA6408GPIOPin::set_inverted(bool inverted) {
|
|
||||||
this->inverted_ = inverted;
|
|
||||||
if (this->parent_) {
|
|
||||||
this->parent_->set_polarity_inversion(this->pin_, inverted);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TCA6408GPIOPin::pin_mode(gpio::Flags flags) {
|
void TCA6408GPIOPin::pin_mode(gpio::Flags flags) {
|
||||||
@@ -187,16 +159,20 @@ void TCA6408GPIOPin::pin_mode(gpio::Flags flags) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool TCA6408GPIOPin::digital_read() {
|
bool TCA6408GPIOPin::digital_read() {
|
||||||
// Note: inversion handled in digital_read_cache() for inputs
|
return this->parent_->digital_read_cache(this->pin_) != this->inverted_;
|
||||||
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->inverted_);
|
this->parent_->digital_write_hw(this->pin_, value != this->inverted_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCA6408GPIOPin::set_inverted(bool inverted) {
|
||||||
|
this->inverted_ = inverted;
|
||||||
|
this->parent_->set_polarity_inversion(this->pin_, 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, "TCA6408 pin %u", this->pin_);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tca6408
|
} // namespace tca6408
|
||||||
|
|||||||
Reference in New Issue
Block a user