[mcp23xxx] Use CachedGpioExpander (#10078)

This commit is contained in:
Jesse Hills 2025-08-06 11:01:57 +12:00 committed by GitHub
parent c308e03e92
commit 3edd746c6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 89 additions and 50 deletions

View File

@ -24,5 +24,5 @@ CONFIG_SCHEMA = (
async def to_code(config): async def to_code(config):
var = await mcp23xxx_base.register_mcp23xxx(config) var = await mcp23xxx_base.register_mcp23xxx(config, mcp23x08_base.NUM_PINS)
await i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)

View File

@ -24,5 +24,5 @@ CONFIG_SCHEMA = (
async def to_code(config): async def to_code(config):
var = await mcp23xxx_base.register_mcp23xxx(config) var = await mcp23xxx_base.register_mcp23xxx(config, mcp23x17_base.NUM_PINS)
await i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)

View File

@ -27,6 +27,6 @@ CONFIG_SCHEMA = (
async def to_code(config): async def to_code(config):
var = await mcp23xxx_base.register_mcp23xxx(config) var = await mcp23xxx_base.register_mcp23xxx(config, mcp23x08_base.NUM_PINS)
cg.add(var.set_device_address(config[CONF_DEVICEADDRESS])) cg.add(var.set_device_address(config[CONF_DEVICEADDRESS]))
await spi.register_spi_device(var, config) await spi.register_spi_device(var, config)

View File

@ -27,6 +27,6 @@ CONFIG_SCHEMA = (
async def to_code(config): async def to_code(config):
var = await mcp23xxx_base.register_mcp23xxx(config) var = await mcp23xxx_base.register_mcp23xxx(config, mcp23x17_base.NUM_PINS)
cg.add(var.set_device_address(config[CONF_DEVICEADDRESS])) cg.add(var.set_device_address(config[CONF_DEVICEADDRESS]))
await spi.register_spi_device(var, config) await spi.register_spi_device(var, config)

View File

@ -4,5 +4,7 @@ from esphome.components import mcp23xxx_base
AUTO_LOAD = ["mcp23xxx_base"] AUTO_LOAD = ["mcp23xxx_base"]
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]
NUM_PINS = 8
mcp23x08_base_ns = cg.esphome_ns.namespace("mcp23x08_base") mcp23x08_base_ns = cg.esphome_ns.namespace("mcp23x08_base")
MCP23X08Base = mcp23x08_base_ns.class_("MCP23X08Base", mcp23xxx_base.MCP23XXXBase) MCP23X08Base = mcp23x08_base_ns.class_("MCP23X08Base", mcp23xxx_base.MCP23XXXBase)

View File

@ -6,19 +6,21 @@ namespace mcp23x08_base {
static const char *const TAG = "mcp23x08_base"; static const char *const TAG = "mcp23x08_base";
bool MCP23X08Base::digital_read(uint8_t pin) { bool MCP23X08Base::digital_read_hw(uint8_t pin) {
uint8_t bit = pin % 8; if (!this->read_reg(mcp23x08_base::MCP23X08_GPIO, &this->input_mask_)) {
uint8_t reg_addr = mcp23x08_base::MCP23X08_GPIO; this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
uint8_t value = 0; return false;
this->read_reg(reg_addr, &value); }
return value & (1 << bit); return true;
} }
void MCP23X08Base::digital_write(uint8_t pin, bool value) { void MCP23X08Base::digital_write_hw(uint8_t pin, bool value) {
uint8_t reg_addr = mcp23x08_base::MCP23X08_OLAT; uint8_t reg_addr = mcp23x08_base::MCP23X08_OLAT;
this->update_reg(pin, value, reg_addr); this->update_reg(pin, value, reg_addr);
} }
bool MCP23X08Base::digital_read_cache(uint8_t pin) { return this->input_mask_ & (1 << pin); }
void MCP23X08Base::pin_mode(uint8_t pin, gpio::Flags flags) { void MCP23X08Base::pin_mode(uint8_t pin, gpio::Flags flags) {
uint8_t iodir = mcp23x08_base::MCP23X08_IODIR; uint8_t iodir = mcp23x08_base::MCP23X08_IODIR;
uint8_t gppu = mcp23x08_base::MCP23X08_GPPU; uint8_t gppu = mcp23x08_base::MCP23X08_GPPU;

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "esphome/core/component.h"
#include "esphome/components/mcp23xxx_base/mcp23xxx_base.h" #include "esphome/components/mcp23xxx_base/mcp23xxx_base.h"
#include "esphome/core/component.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
namespace esphome { namespace esphome {
@ -22,10 +22,12 @@ enum MCP23S08GPIORegisters {
MCP23X08_OLAT = 0x0A, MCP23X08_OLAT = 0x0A,
}; };
class MCP23X08Base : public mcp23xxx_base::MCP23XXXBase { class MCP23X08Base : public mcp23xxx_base::MCP23XXXBase<8> {
public: public:
bool digital_read(uint8_t pin) override; bool digital_read_hw(uint8_t pin) override;
void digital_write(uint8_t pin, bool value) override; void digital_write_hw(uint8_t pin, bool value) override;
bool digital_read_cache(uint8_t pin) override;
void pin_mode(uint8_t pin, gpio::Flags flags) override; void pin_mode(uint8_t pin, gpio::Flags flags) override;
void pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) override; void pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) override;
@ -33,6 +35,9 @@ class MCP23X08Base : public mcp23xxx_base::MCP23XXXBase {
void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a) override; void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a) override;
uint8_t olat_{0x00}; uint8_t olat_{0x00};
/// State read in digital_read_hw
uint8_t input_mask_{0x00};
}; };
} // namespace mcp23x08_base } // namespace mcp23x08_base

View File

@ -4,5 +4,7 @@ from esphome.components import mcp23xxx_base
AUTO_LOAD = ["mcp23xxx_base"] AUTO_LOAD = ["mcp23xxx_base"]
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]
NUM_PINS = 16
mcp23x17_base_ns = cg.esphome_ns.namespace("mcp23x17_base") mcp23x17_base_ns = cg.esphome_ns.namespace("mcp23x17_base")
MCP23X17Base = mcp23x17_base_ns.class_("MCP23X17Base", mcp23xxx_base.MCP23XXXBase) MCP23X17Base = mcp23x17_base_ns.class_("MCP23X17Base", mcp23xxx_base.MCP23XXXBase)

View File

@ -1,4 +1,5 @@
#include "mcp23x17_base.h" #include "mcp23x17_base.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome { namespace esphome {
@ -6,19 +7,31 @@ namespace mcp23x17_base {
static const char *const TAG = "mcp23x17_base"; static const char *const TAG = "mcp23x17_base";
bool MCP23X17Base::digital_read(uint8_t pin) { bool MCP23X17Base::digital_read_hw(uint8_t pin) {
uint8_t bit = pin % 8; uint8_t data;
uint8_t reg_addr = pin < 8 ? mcp23x17_base::MCP23X17_GPIOA : mcp23x17_base::MCP23X17_GPIOB; if (pin < 8) {
uint8_t value = 0; if (!this->read_reg(mcp23x17_base::MCP23X17_GPIOA, &data)) {
this->read_reg(reg_addr, &value); this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
return value & (1 << bit); return false;
}
this->input_mask_ = encode_uint16(this->input_mask_ >> 8, data);
} else {
if (!this->read_reg(mcp23x17_base::MCP23X17_GPIOB, &data)) {
this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
return false;
}
this->input_mask_ = encode_uint16(data, this->input_mask_ & 0xFF);
}
return true;
} }
void MCP23X17Base::digital_write(uint8_t pin, bool value) { void MCP23X17Base::digital_write_hw(uint8_t pin, bool value) {
uint8_t reg_addr = pin < 8 ? mcp23x17_base::MCP23X17_OLATA : mcp23x17_base::MCP23X17_OLATB; uint8_t reg_addr = pin < 8 ? mcp23x17_base::MCP23X17_OLATA : mcp23x17_base::MCP23X17_OLATB;
this->update_reg(pin, value, reg_addr); this->update_reg(pin, value, reg_addr);
} }
bool MCP23X17Base::digital_read_cache(uint8_t pin) { return this->input_mask_ & (1 << pin); }
void MCP23X17Base::pin_mode(uint8_t pin, gpio::Flags flags) { void MCP23X17Base::pin_mode(uint8_t pin, gpio::Flags flags) {
uint8_t iodir = pin < 8 ? mcp23x17_base::MCP23X17_IODIRA : mcp23x17_base::MCP23X17_IODIRB; uint8_t iodir = pin < 8 ? mcp23x17_base::MCP23X17_IODIRA : mcp23x17_base::MCP23X17_IODIRB;
uint8_t gppu = pin < 8 ? mcp23x17_base::MCP23X17_GPPUA : mcp23x17_base::MCP23X17_GPPUB; uint8_t gppu = pin < 8 ? mcp23x17_base::MCP23X17_GPPUA : mcp23x17_base::MCP23X17_GPPUB;

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "esphome/core/component.h"
#include "esphome/components/mcp23xxx_base/mcp23xxx_base.h" #include "esphome/components/mcp23xxx_base/mcp23xxx_base.h"
#include "esphome/core/component.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
namespace esphome { namespace esphome {
@ -34,10 +34,12 @@ enum MCP23X17GPIORegisters {
MCP23X17_OLATB = 0x15, MCP23X17_OLATB = 0x15,
}; };
class MCP23X17Base : public mcp23xxx_base::MCP23XXXBase { class MCP23X17Base : public mcp23xxx_base::MCP23XXXBase<16> {
public: public:
bool digital_read(uint8_t pin) override; bool digital_read_hw(uint8_t pin) override;
void digital_write(uint8_t pin, bool value) override; void digital_write_hw(uint8_t pin, bool value) override;
bool digital_read_cache(uint8_t pin) override;
void pin_mode(uint8_t pin, gpio::Flags flags) override; void pin_mode(uint8_t pin, gpio::Flags flags) override;
void pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) override; void pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) override;
@ -46,6 +48,9 @@ class MCP23X17Base : public mcp23xxx_base::MCP23XXXBase {
uint8_t olat_a_{0x00}; uint8_t olat_a_{0x00};
uint8_t olat_b_{0x00}; uint8_t olat_b_{0x00};
/// State read in digital_read_hw
uint16_t input_mask_{0x00};
}; };
} // namespace mcp23x17_base } // namespace mcp23x17_base

View File

@ -12,8 +12,9 @@ from esphome.const import (
CONF_OUTPUT, CONF_OUTPUT,
CONF_PULLUP, CONF_PULLUP,
) )
from esphome.core import coroutine from esphome.core import CORE, ID, coroutine
AUTO_LOAD = ["gpio_expander"]
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]
mcp23xxx_base_ns = cg.esphome_ns.namespace("mcp23xxx_base") mcp23xxx_base_ns = cg.esphome_ns.namespace("mcp23xxx_base")
@ -36,9 +37,11 @@ MCP23XXX_CONFIG_SCHEMA = cv.Schema(
@coroutine @coroutine
async def register_mcp23xxx(config): async def register_mcp23xxx(config, num_pins):
var = cg.new_Pvariable(config[CONF_ID]) id: ID = config[CONF_ID]
var = cg.new_Pvariable(id)
await cg.register_component(var, config) await cg.register_component(var, config)
CORE.data.setdefault(CONF_MCP23XXX, {})[id.id] = num_pins
cg.add(var.set_open_drain_ints(config[CONF_OPEN_DRAIN_INTERRUPT])) cg.add(var.set_open_drain_ints(config[CONF_OPEN_DRAIN_INTERRUPT]))
return var return var
@ -73,9 +76,12 @@ MCP23XXX_PIN_SCHEMA = pins.gpio_base_schema(
@pins.PIN_SCHEMA_REGISTRY.register(CONF_MCP23XXX, MCP23XXX_PIN_SCHEMA) @pins.PIN_SCHEMA_REGISTRY.register(CONF_MCP23XXX, MCP23XXX_PIN_SCHEMA)
async def mcp23xxx_pin_to_code(config): async def mcp23xxx_pin_to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) parent_id: ID = config[CONF_MCP23XXX]
parent = await cg.get_variable(config[CONF_MCP23XXX]) parent = await cg.get_variable(parent_id)
num_pins = cg.TemplateArguments(CORE.data[CONF_MCP23XXX][parent_id.id])
var = cg.new_Pvariable(config[CONF_ID], num_pins)
cg.add(var.set_parent(parent)) cg.add(var.set_parent(parent))
num = config[CONF_NUMBER] num = config[CONF_NUMBER]

View File

@ -1,24 +1,27 @@
#include "mcp23xxx_base.h" #include "mcp23xxx_base.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome { namespace esphome {
namespace mcp23xxx_base { namespace mcp23xxx_base {
float MCP23XXXBase::get_setup_priority() const { return setup_priority::IO; } template<uint8_t N> void MCP23XXXGPIOPin<N>::setup() {
this->pin_mode(flags_);
void MCP23XXXGPIOPin::setup() {
pin_mode(flags_);
this->parent_->pin_interrupt_mode(this->pin_, this->interrupt_mode_); this->parent_->pin_interrupt_mode(this->pin_, this->interrupt_mode_);
} }
template<uint8_t N> void MCP23XXXGPIOPin<N>::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
void MCP23XXXGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } template<uint8_t N> bool MCP23XXXGPIOPin<N>::digital_read() {
bool MCP23XXXGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } return this->parent_->digital_read(this->pin_) != this->inverted_;
void MCP23XXXGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
std::string MCP23XXXGPIOPin::dump_summary() const {
char buffer[32];
snprintf(buffer, sizeof(buffer), "%u via MCP23XXX", pin_);
return buffer;
} }
template<uint8_t N> void MCP23XXXGPIOPin<N>::digital_write(bool value) {
this->parent_->digital_write(this->pin_, value != this->inverted_);
}
template<uint8_t N> std::string MCP23XXXGPIOPin<N>::dump_summary() const {
return str_snprintf("%u via MCP23XXX", 15, pin_);
}
template class MCP23XXXGPIOPin<8>;
template class MCP23XXXGPIOPin<16>;
} // namespace mcp23xxx_base } // namespace mcp23xxx_base
} // namespace esphome } // namespace esphome

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "esphome/components/gpio_expander/cached_gpio.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
@ -8,15 +9,15 @@ namespace mcp23xxx_base {
enum MCP23XXXInterruptMode : uint8_t { MCP23XXX_NO_INTERRUPT = 0, MCP23XXX_CHANGE, MCP23XXX_RISING, MCP23XXX_FALLING }; enum MCP23XXXInterruptMode : uint8_t { MCP23XXX_NO_INTERRUPT = 0, MCP23XXX_CHANGE, MCP23XXX_RISING, MCP23XXX_FALLING };
class MCP23XXXBase : public Component { template<uint8_t N> class MCP23XXXBase : public Component, public gpio_expander::CachedGpioExpander<uint8_t, N> {
public: public:
virtual bool digital_read(uint8_t pin);
virtual void digital_write(uint8_t pin, bool value);
virtual void pin_mode(uint8_t pin, gpio::Flags flags); virtual void pin_mode(uint8_t pin, gpio::Flags flags);
virtual void pin_interrupt_mode(uint8_t pin, MCP23XXXInterruptMode interrupt_mode); virtual void pin_interrupt_mode(uint8_t pin, MCP23XXXInterruptMode interrupt_mode);
void set_open_drain_ints(const bool value) { this->open_drain_ints_ = value; } void set_open_drain_ints(const bool value) { this->open_drain_ints_ = value; }
float get_setup_priority() const override; float get_setup_priority() const override { return setup_priority::IO; }
void loop() override { this->reset_pin_cache_(); }
protected: protected:
// read a given register // read a given register
@ -29,7 +30,7 @@ class MCP23XXXBase : public Component {
bool open_drain_ints_; bool open_drain_ints_;
}; };
class MCP23XXXGPIOPin : public GPIOPin { template<uint8_t N> class MCP23XXXGPIOPin : public GPIOPin {
public: public:
void setup() override; void setup() override;
void pin_mode(gpio::Flags flags) override; void pin_mode(gpio::Flags flags) override;
@ -37,7 +38,7 @@ class MCP23XXXGPIOPin : public GPIOPin {
void digital_write(bool value) override; void digital_write(bool value) override;
std::string dump_summary() const override; std::string dump_summary() const override;
void set_parent(MCP23XXXBase *parent) { parent_ = parent; } void set_parent(MCP23XXXBase<N> *parent) { parent_ = parent; }
void set_pin(uint8_t pin) { pin_ = pin; } void set_pin(uint8_t pin) { pin_ = pin; }
void set_inverted(bool inverted) { inverted_ = inverted; } void set_inverted(bool inverted) { inverted_ = inverted; }
void set_flags(gpio::Flags flags) { flags_ = flags; } void set_flags(gpio::Flags flags) { flags_ = flags; }
@ -46,7 +47,7 @@ class MCP23XXXGPIOPin : public GPIOPin {
gpio::Flags get_flags() const override { return this->flags_; } gpio::Flags get_flags() const override { return this->flags_; }
protected: protected:
MCP23XXXBase *parent_; MCP23XXXBase<N> *parent_;
uint8_t pin_; uint8_t pin_;
bool inverted_; bool inverted_;
gpio::Flags flags_; gpio::Flags flags_;