mirror of
https://github.com/esphome/esphome.git
synced 2025-11-20 16:55:49 +00:00
Implements support for the TI TCAL6416 16-bit I2C I/O expander with the following features: - 16 GPIO pins (2 banks of 8) - Bidirectional I/O with configurable input/output modes - Support for inverted pins - Compatible with ESP32, ESP8266, and RP2040 platforms - I2C interface with configurable address (default: 0x20) The TCAL6416 provides higher current latched outputs suitable for directly driving LEDs or keypads. It operates at voltages from 1.08V to 3.6V and supports I2C speeds up to 1MHz. Addresses: https://github.com/orgs/esphome/discussions/3233
156 lines
4.6 KiB
C++
156 lines
4.6 KiB
C++
#include "tcal6416.h"
|
|
#include "esphome/core/log.h"
|
|
|
|
static const uint8_t TCAL6416_INPUT_PORT_REGISTER_0 = 0x00;
|
|
static const uint8_t TCAL6416_INPUT_PORT_REGISTER_1 = 0x01;
|
|
static const uint8_t TCAL6416_OUTPUT_PORT_REGISTER_0 = 0x02;
|
|
static const uint8_t TCAL6416_OUTPUT_PORT_REGISTER_1 = 0x03;
|
|
static const uint8_t TCAL6416_POLARITY_REGISTER_0 = 0x04;
|
|
static const uint8_t TCAL6416_POLARITY_REGISTER_1 = 0x05;
|
|
static const uint8_t TCAL6416_CONFIGURATION_PORT_0 = 0x06;
|
|
static const uint8_t TCAL6416_CONFIGURATION_PORT_1 = 0x07;
|
|
|
|
namespace esphome {
|
|
namespace tcal6416 {
|
|
|
|
static const char *const TAG = "tcal6416";
|
|
|
|
void TCAL6416Component::setup() {
|
|
if (!this->read_gpio_modes_()) {
|
|
this->mark_failed();
|
|
return;
|
|
}
|
|
if (!this->read_gpio_outputs_()) {
|
|
this->mark_failed();
|
|
return;
|
|
}
|
|
|
|
// Disable polarity inversion
|
|
uint8_t data[2] = {0x00, 0x00};
|
|
if (!this->write_bytes(TCAL6416_POLARITY_REGISTER_0, data, 2)) {
|
|
this->mark_failed();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void TCAL6416Component::dump_config() {
|
|
ESP_LOGCONFIG(TAG, "TCAL6416:");
|
|
LOG_I2C_DEVICE(this)
|
|
if (this->is_failed()) {
|
|
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
|
}
|
|
}
|
|
|
|
void TCAL6416Component::pin_mode(uint8_t pin, gpio::Flags flags) {
|
|
if (flags == gpio::FLAG_INPUT) {
|
|
// Set mode mask bit (1 = input)
|
|
this->mode_mask_ |= 1 << pin;
|
|
} else if (flags == gpio::FLAG_OUTPUT) {
|
|
// Clear mode mask bit (0 = output)
|
|
this->mode_mask_ &= ~(1 << pin);
|
|
}
|
|
// Write GPIO to enable input mode
|
|
this->write_gpio_modes_();
|
|
}
|
|
|
|
void TCAL6416Component::loop() { this->reset_pin_cache_(); }
|
|
|
|
bool TCAL6416Component::read_gpio_outputs_() {
|
|
if (this->is_failed())
|
|
return false;
|
|
uint8_t data[2];
|
|
if (!this->read_bytes(TCAL6416_OUTPUT_PORT_REGISTER_0, data, 2)) {
|
|
this->status_set_warning(LOG_STR("Failed to read output register"));
|
|
return false;
|
|
}
|
|
this->output_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0);
|
|
this->status_clear_warning();
|
|
return true;
|
|
}
|
|
|
|
bool TCAL6416Component::read_gpio_modes_() {
|
|
if (this->is_failed())
|
|
return false;
|
|
uint8_t data[2];
|
|
bool success = this->read_bytes(TCAL6416_CONFIGURATION_PORT_0, data, 2);
|
|
if (!success) {
|
|
this->status_set_warning(LOG_STR("Failed to read mode register"));
|
|
return false;
|
|
}
|
|
this->mode_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0);
|
|
|
|
this->status_clear_warning();
|
|
return true;
|
|
}
|
|
|
|
bool TCAL6416Component::digital_read_hw(uint8_t pin) {
|
|
if (this->is_failed())
|
|
return false;
|
|
uint8_t data;
|
|
uint8_t bank_number = pin < 8 ? 0 : 1;
|
|
uint8_t register_to_read = bank_number ? TCAL6416_INPUT_PORT_REGISTER_1 : TCAL6416_INPUT_PORT_REGISTER_0;
|
|
if (!this->read_bytes(register_to_read, &data, 1)) {
|
|
this->status_set_warning(LOG_STR("Failed to read input register"));
|
|
return false;
|
|
}
|
|
uint8_t second_half = this->input_mask_ >> 8;
|
|
uint8_t first_half = this->input_mask_;
|
|
if (bank_number) {
|
|
this->input_mask_ = (data << 8) | (uint16_t(first_half) << 0);
|
|
} else {
|
|
this->input_mask_ = (uint16_t(second_half) << 8) | (data << 0);
|
|
}
|
|
|
|
this->status_clear_warning();
|
|
return true;
|
|
}
|
|
|
|
void TCAL6416Component::digital_write_hw(uint8_t pin, bool value) {
|
|
if (this->is_failed())
|
|
return;
|
|
|
|
if (value) {
|
|
this->output_mask_ |= (1 << pin);
|
|
} else {
|
|
this->output_mask_ &= ~(1 << pin);
|
|
}
|
|
|
|
uint8_t data[2];
|
|
data[0] = this->output_mask_;
|
|
data[1] = this->output_mask_ >> 8;
|
|
if (!this->write_bytes(TCAL6416_OUTPUT_PORT_REGISTER_0, data, 2)) {
|
|
this->status_set_warning(LOG_STR("Failed to write output register"));
|
|
return;
|
|
}
|
|
|
|
this->status_clear_warning();
|
|
}
|
|
|
|
bool TCAL6416Component::write_gpio_modes_() {
|
|
if (this->is_failed())
|
|
return false;
|
|
uint8_t data[2];
|
|
|
|
data[0] = this->mode_mask_;
|
|
data[1] = this->mode_mask_ >> 8;
|
|
if (!this->write_bytes(TCAL6416_CONFIGURATION_PORT_0, data, 2)) {
|
|
this->status_set_warning(LOG_STR("Failed to write mode register"));
|
|
return false;
|
|
}
|
|
this->status_clear_warning();
|
|
return true;
|
|
}
|
|
|
|
bool TCAL6416Component::digital_read_cache(uint8_t pin) { return this->input_mask_ & (1 << pin); }
|
|
|
|
float TCAL6416Component::get_setup_priority() const { return setup_priority::IO; }
|
|
|
|
void TCAL6416GPIOPin::setup() { this->pin_mode(this->flags_); }
|
|
void TCAL6416GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
|
|
bool TCAL6416GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
|
|
void TCAL6416GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
|
|
std::string TCAL6416GPIOPin::dump_summary() const { return str_sprintf("%u via TCAL6416", this->pin_); }
|
|
|
|
} // namespace tcal6416
|
|
} // namespace esphome
|