From 6e2c3f4981872a1a93333bf12273d0b4ae266926 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 4 Sep 2025 15:35:26 -0500 Subject: [PATCH] [max6956] Migrate to CachedGpioExpander to reduce I2C bus usage --- esphome/components/max6956/__init__.py | 1 + esphome/components/max6956/max6956.cpp | 57 ++++++++++++++++++++++---- esphome/components/max6956/max6956.h | 22 ++++++++-- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/esphome/components/max6956/__init__.py b/esphome/components/max6956/__init__.py index 0d2ff527c7d..299a9ad704b 100644 --- a/esphome/components/max6956/__init__.py +++ b/esphome/components/max6956/__init__.py @@ -15,6 +15,7 @@ from esphome.const import ( CODEOWNERS = ["@looping40"] DEPENDENCIES = ["i2c"] +AUTO_LOAD = ["gpio_expander"] MULTI_CONF = True CONF_BRIGHTNESS_MODE = "brightness_mode" diff --git a/esphome/components/max6956/max6956.cpp b/esphome/components/max6956/max6956.cpp index a377a1a192f..839a6a8951a 100644 --- a/esphome/components/max6956/max6956.cpp +++ b/esphome/components/max6956/max6956.cpp @@ -40,14 +40,55 @@ void MAX6956::setup() { ESP_LOGD(TAG, "setup reg[0x%.2X]=0x%.2X", MAX6956_CONFIGURATION, configuration); } -bool MAX6956::digital_read(uint8_t pin) { - uint8_t reg_addr = MAX6956_1PORT_VALUE_START + pin; - uint8_t value = 0; - this->read_reg_(reg_addr, &value); - return (value & MASK_1PORT_VALUE); +void MAX6956::loop() { + // Invalidate cache at the start of each loop + this->reset_pin_cache_(); } -void MAX6956::digital_write(uint8_t pin, bool value) { +bool MAX6956::digital_read_hw(uint8_t pin) { + // MAX6956 pins start at MAX6956_MIN + if (pin < MAX6956_MIN || pin > MAX6956_MAX) { + return false; + } + + // Calculate bank index + uint8_t bank_index = (pin - MAX6956_MIN) / MAX6956_BANK_SIZE; + + // Register addresses for each bank + static const uint8_t bank_regs[4] = { + MAX6956_8PORTS_VALUE_START, // 0x44 - ports 4-11 + MAX6956_8PORTS_12_19, // 0x4C - ports 12-19 + MAX6956_8PORTS_20_27, // 0x54 - ports 20-27 + MAX6956_8PORTS_24_31 // 0x58 - ports 24-31 + }; + + // Read the 8-port bank register + uint8_t bank_value = 0; + if (!this->read_reg_(bank_regs[bank_index], &bank_value)) { + return false; + } + + // Store the bank value in our cache + this->input_banks_[bank_index] = bank_value; + + return true; +} + +bool MAX6956::digital_read_cache(uint8_t pin) { + // MAX6956 pins start at MAX6956_MIN + if (pin < MAX6956_MIN || pin > MAX6956_MAX) { + return false; + } + + // Calculate bank index and bit position + uint8_t adjusted_pin = pin - MAX6956_MIN; + uint8_t bank_index = adjusted_pin / MAX6956_BANK_SIZE; + uint8_t bit_position = adjusted_pin % MAX6956_BANK_SIZE; + + return (this->input_banks_[bank_index] & (1 << bit_position)) != 0; +} + +void MAX6956::digital_write_hw(uint8_t pin, bool value) { uint8_t reg_addr = MAX6956_1PORT_VALUE_START + pin; this->write_reg_(reg_addr, value); } @@ -159,8 +200,8 @@ void MAX6956::dump_config() { **************************************/ void MAX6956GPIOPin::setup() { pin_mode(flags_); } void MAX6956GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } -bool MAX6956GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } -void MAX6956GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } +bool MAX6956GPIOPin::digital_read() { return this->parent_->read_pin_cached(this->pin_) != this->inverted_; } +void MAX6956GPIOPin::digital_write(bool value) { this->parent_->write_pin(this->pin_, value != this->inverted_); } std::string MAX6956GPIOPin::dump_summary() const { char buffer[32]; snprintf(buffer, sizeof(buffer), "%u via Max6956", pin_); diff --git a/esphome/components/max6956/max6956.h b/esphome/components/max6956/max6956.h index 0a1fd5e4b59..f45f7ab62a5 100644 --- a/esphome/components/max6956/max6956.h +++ b/esphome/components/max6956/max6956.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/core/hal.h" #include "esphome/components/i2c/i2c.h" +#include "esphome/components/gpio_expander/cached_gpio.h" namespace esphome { namespace max6956 { @@ -21,6 +22,9 @@ enum MAX6956GPIORange : uint8_t { MAX6956_MAX = 31, }; +/// Bank configuration for MAX6956 +static constexpr uint8_t MAX6956_BANK_SIZE = 8; + enum MAX6956GPIORegisters { MAX6956_GLOBAL_CURRENT = 0x02, MAX6956_CONFIGURATION = 0x04, @@ -30,20 +34,23 @@ enum MAX6956GPIORegisters { MAX6956_CURRENT_START = 0x12, // Current054 MAX6956_1PORT_VALUE_START = 0x20, // Port 0 only (virtual port, no action) MAX6956_8PORTS_VALUE_START = 0x44, // 8 ports 4-11 (data bits D0-D7) + // Additional 8-port bulk read registers + MAX6956_8PORTS_12_19 = 0x4C, // 8 ports 12-19 + MAX6956_8PORTS_20_27 = 0x54, // 8 ports 20-27 + MAX6956_8PORTS_24_31 = 0x58, // 8 ports 24-31 }; enum MAX6956GPIOFlag { FLAG_LED = 0x20 }; enum MAX6956CURRENTMODE { GLOBAL = 0x00, SEGMENT = 0x01 }; -class MAX6956 : public Component, public i2c::I2CDevice { +class MAX6956 : public Component, public i2c::I2CDevice, public gpio_expander::CachedGpioExpander { public: MAX6956() = default; void setup() override; + void loop() override; - bool digital_read(uint8_t pin); - void digital_write(uint8_t pin, bool value); void pin_mode(uint8_t pin, gpio::Flags flags); void pin_mode(uint8_t pin, max6956::MAX6956GPIOFlag flags); @@ -58,6 +65,11 @@ class MAX6956 : public Component, public i2c::I2CDevice { void write_brightness_global(); void write_brightness_mode(); + // CachedGpioExpander implementation + bool digital_read_hw(uint8_t pin) override; + bool digital_read_cache(uint8_t pin) override; + void digital_write_hw(uint8_t pin, bool value) override; + protected: // read a given register bool read_reg_(uint8_t reg, uint8_t *value); @@ -66,6 +78,10 @@ class MAX6956 : public Component, public i2c::I2CDevice { max6956::MAX6956CURRENTMODE brightness_mode_; uint8_t global_brightness_; + // Cache for the 4 banks of 8 pins each + // Bank 0: pins 4-11, Bank 1: pins 12-19, Bank 2: pins 20-27, Bank 3: pins 24-31 + uint8_t input_banks_[4] = {0, 0, 0, 0}; + private: int8_t prev_bright_[28] = {0}; };