Fix MCP23x17 MIRROR bit, add dump_config logging, and clear PI4IOE5V6408 latched interrupts

- MCP23017/MCP23S17: Enable MIRROR bit in IOCON when interrupt_pin is
  configured so INTA and INTB are ORed together, allowing a single
  interrupt wire to catch changes on all 16 pins
- All MCP23xxx variants: Log interrupt_pin in dump_config for debugging
- PI4IOE5V6408: Read interrupt status register during setup to clear
  any latched state that could hold INT line low
- MCP23XXXBase: Move setup_interrupt_pin_() to protected visibility
This commit is contained in:
J. Nick Koston
2026-04-04 10:03:40 -10:00
parent 8234512a89
commit 85036d64d9
6 changed files with 51 additions and 25 deletions
+5 -2
View File
@@ -23,10 +23,13 @@ void MCP23008::setup() {
this->write_reg(mcp23x08_base::MCP23X08_IOCON, iocon | IOCON_ODR);
}
this->setup_interrupt_pin();
this->setup_interrupt_pin_();
}
void MCP23008::dump_config() { ESP_LOGCONFIG(TAG, "MCP23008:"); }
void MCP23008::dump_config() {
ESP_LOGCONFIG(TAG, "MCP23008:");
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
}
bool MCP23008::read_reg(uint8_t reg, uint8_t *value) {
if (this->is_failed())
+17 -6
View File
@@ -6,7 +6,8 @@ namespace mcp23017 {
static const char *const TAG = "mcp23017";
static constexpr uint8_t IOCON_ODR = 0x04; // Open-drain output for INT pin
static constexpr uint8_t IOCON_MIRROR = 0x40; // Mirror INTA/INTB pins
static constexpr uint8_t IOCON_ODR = 0x04; // Open-drain output for INT pin
void MCP23017::setup() {
uint8_t iocon;
@@ -19,16 +20,26 @@ void MCP23017::setup() {
this->read_reg(mcp23x17_base::MCP23X17_OLATA, &this->olat_a_);
this->read_reg(mcp23x17_base::MCP23X17_OLATB, &this->olat_b_);
uint8_t iocon_flags = 0;
if (this->open_drain_ints_) {
// enable open-drain interrupt pins, 3.3V-safe
this->write_reg(mcp23x17_base::MCP23X17_IOCONA, iocon | IOCON_ODR);
this->write_reg(mcp23x17_base::MCP23X17_IOCONB, iocon | IOCON_ODR);
iocon_flags |= IOCON_ODR;
}
if (this->interrupt_pin_ != nullptr) {
// Mirror INTA/INTB so either pin fires for changes on any port
iocon_flags |= IOCON_MIRROR;
}
if (iocon_flags != 0) {
this->write_reg(mcp23x17_base::MCP23X17_IOCONA, iocon | iocon_flags);
this->write_reg(mcp23x17_base::MCP23X17_IOCONB, iocon | iocon_flags);
}
this->setup_interrupt_pin();
this->setup_interrupt_pin_();
}
void MCP23017::dump_config() { ESP_LOGCONFIG(TAG, "MCP23017:"); }
void MCP23017::dump_config() {
ESP_LOGCONFIG(TAG, "MCP23017:");
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
}
bool MCP23017::read_reg(uint8_t reg, uint8_t *value) {
if (this->is_failed())
+2 -1
View File
@@ -35,12 +35,13 @@ void MCP23S08::setup() {
this->write_reg(mcp23x08_base::MCP23X08_IOCON, IOCON_SEQOP | IOCON_HAEN | IOCON_ODR);
}
this->setup_interrupt_pin();
this->setup_interrupt_pin_();
}
void MCP23S08::dump_config() {
ESP_LOGCONFIG(TAG, "MCP23S08:");
LOG_PIN(" CS Pin: ", this->cs_);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
}
bool MCP23S08::read_reg(uint8_t reg, uint8_t *value) {
+16 -7
View File
@@ -7,9 +7,10 @@ namespace mcp23s17 {
static const char *const TAG = "mcp23s17";
// IOCON register bits
static constexpr uint8_t IOCON_SEQOP = 0x20; // Sequential operation mode
static constexpr uint8_t IOCON_HAEN = 0x08; // Hardware address enable
static constexpr uint8_t IOCON_ODR = 0x04; // Open-drain output for INT pin
static constexpr uint8_t IOCON_SEQOP = 0x20; // Sequential operation mode
static constexpr uint8_t IOCON_MIRROR = 0x40; // Mirror INTA/INTB pins
static constexpr uint8_t IOCON_HAEN = 0x08; // Hardware address enable
static constexpr uint8_t IOCON_ODR = 0x04; // Open-drain output for INT pin
void MCP23S17::set_device_address(uint8_t device_addr) {
if (device_addr != 0) {
@@ -37,18 +38,26 @@ void MCP23S17::setup() {
this->read_reg(mcp23x17_base::MCP23X17_OLATA, &this->olat_a_);
this->read_reg(mcp23x17_base::MCP23X17_OLATB, &this->olat_b_);
uint8_t iocon_flags = IOCON_SEQOP | IOCON_HAEN;
if (this->open_drain_ints_) {
// enable open-drain interrupt pins, 3.3V-safe (addressed, only this chip)
this->write_reg(mcp23x17_base::MCP23X17_IOCONA, IOCON_SEQOP | IOCON_HAEN | IOCON_ODR);
this->write_reg(mcp23x17_base::MCP23X17_IOCONB, IOCON_SEQOP | IOCON_HAEN | IOCON_ODR);
iocon_flags |= IOCON_ODR;
}
if (this->interrupt_pin_ != nullptr) {
// Mirror INTA/INTB so either pin fires for changes on any port
iocon_flags |= IOCON_MIRROR;
}
if (this->open_drain_ints_ || this->interrupt_pin_ != nullptr) {
this->write_reg(mcp23x17_base::MCP23X17_IOCONA, iocon_flags);
this->write_reg(mcp23x17_base::MCP23X17_IOCONB, iocon_flags);
}
this->setup_interrupt_pin();
this->setup_interrupt_pin_();
}
void MCP23S17::dump_config() {
ESP_LOGCONFIG(TAG, "MCP23S17:");
LOG_PIN(" CS Pin: ", this->cs_);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
}
bool MCP23S17::read_reg(uint8_t reg, uint8_t *value) {
@@ -18,15 +18,6 @@ template<uint8_t N> class MCP23XXXBase : public Component, public gpio_expander:
void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; }
float get_setup_priority() const override { return setup_priority::IO; }
void setup_interrupt_pin() {
if (this->interrupt_pin_ != nullptr) {
this->interrupt_pin_->setup();
this->interrupt_pin_->attach_interrupt(&MCP23XXXBase::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE);
this->set_invalidate_on_read_(false);
this->disable_loop();
}
}
void loop() override {
this->reset_pin_cache_();
if (this->interrupt_pin_ != nullptr) {
@@ -35,6 +26,14 @@ template<uint8_t N> class MCP23XXXBase : public Component, public gpio_expander:
}
protected:
void setup_interrupt_pin_() {
if (this->interrupt_pin_ != nullptr) {
this->interrupt_pin_->setup();
this->interrupt_pin_->attach_interrupt(&MCP23XXXBase::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE);
this->set_invalidate_on_read_(false);
this->disable_loop();
}
}
static void IRAM_ATTR gpio_intr(MCP23XXXBase *arg) { arg->enable_loop_soon_any_context(); }
// read a given register
@@ -35,6 +35,9 @@ void PI4IOE5V6408Component::setup() {
}
if (this->interrupt_pin_ != nullptr) {
// Clear any latched interrupt status from before boot
uint8_t status;
this->read_byte(PI4IOE5V6408_REGISTER_INTERRUPT_STATUS, &status);
this->interrupt_pin_->setup();
this->interrupt_pin_->attach_interrupt(&PI4IOE5V6408Component::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE);
this->set_invalidate_on_read_(false);